Chapitre 2

Exercice 2.4

Installation et chargement de ggplot2 :

# Installation du package ggplot2
# install.packages(ggplot2)

# Mise en mƩmoire du package
library(ggplot2)

Mise en mƩmoire du jeu de donnƩes diamonds :

# Chargement du jeu de donnƩes diamonds
data(diamonds)

DƩtermination du nombre de lignes de ce tableau :

# Nombre de lignes de diamonds
diamonds
nrow(diamonds)
[1] 53940
str(diamonds)
tibble [53,940 Ɨ 10] (S3: tbl_df/tbl/data.frame)
 $ carat  : num [1:53940] 0.23 0.21 0.23 0.29 0.31 0.24 0.24 0.26 0.22 0.23 ...
 $ cut    : Ord.factor w/ 5 levels "Fair"<"Good"<..: 5 4 2 4 2 3 3 3 1 3 ...
 $ color  : Ord.factor w/ 7 levels "D"<"E"<"F"<"G"<..: 2 2 2 6 7 7 6 5 2 5 ...
 $ clarity: Ord.factor w/ 8 levels "I1"<"SI2"<"SI1"<..: 2 3 5 4 2 6 7 3 4 5 ...
 $ depth  : num [1:53940] 61.5 59.8 56.9 62.4 63.3 62.8 62.3 61.9 65.1 59.4 ...
 $ table  : num [1:53940] 55 61 65 58 58 57 57 55 61 61 ...
 $ price  : int [1:53940] 326 326 327 334 335 336 336 337 337 338 ...
 $ x      : num [1:53940] 3.95 3.89 4.05 4.2 4.34 3.94 3.95 4.07 3.87 4 ...
 $ y      : num [1:53940] 3.98 3.84 4.07 4.23 4.35 3.96 3.98 4.11 3.78 4.05 ...
 $ z      : num [1:53940] 2.43 2.31 2.31 2.63 2.75 2.48 2.47 2.53 2.49 2.39 ...

CrƩation de diamants_chers contenant uniquement les infos des diamants de plus de 15000 USD :

# Première méthode :
diamants_chers <- diamonds[diamonds$price > 15000, ]

# Deuxième méthode :
diamants_chers <- subset(diamonds, diamonds$price > 15000)

# Troisième méthode
library(dplyr)
diamants_chers <- diamonds %>%
  filter(price > 15000)

diamants_chers

Nombre de diamants dont le prix dƩpasse 15000 USD :

sum(diamonds$price > 15000)
[1] 1655

Proportion de diamants dont le prix dƩpasse 15000 USD

mean(diamonds$price > 15000)
[1] 0.03068224

Tri du tableau par ordre de prix dƩcroissants

## Première méthode
diamants_chers_tri <- diamants_chers[order(diamants_chers$price, decreasing = TRUE), ]

## Seconde mƩthode
diamants_chers_tri <- diamants_chers %>%
  arrange(desc(price))

Affichage des 20 diamants les plus chers

head(diamants_chers_tri, n = 20)
# Une autre faƧon de faire tout Ƨa avec le pipe et les fonctions de dplyr
library(dplyr)
diamonds %>% 
  filter(price >= 15000) %>% 
  arrange(desc(price)) %>% 
  print(n = 20)

Chapitre 3

# Mise en mƩmoire des packages utiles
library(dplyr)
library(nycflights13)
# Affichage du tableau flights dans un onglet de RStudio
View(flights)

Exercices 3.3.1

  • Chacune des lignes du tableau corrspond aux donnĆ©es d’un vol (rĆ©ponse B)
  • Nombre de vols ayant dĆ©collĆ© de JFK le 12 fĆ©vrier 2013 : 282. On le dĆ©termine en ajustant les sliders, les filtres, etc.

Exercices 3.3.2

Les variables catƩgorielles sont :

  1. tailnum (numĆ©ro d’identification de l’avion),
  2. origin (aĆ©roport de dĆ©part de l’avion),
  3. dest (aĆ©roport de destination de l’avion),
  4. carrier (compagnie aƩrienne)

Les valeurs sont des chaînes de caractères et non des chiffres.

Exercices 3.4

Consultez l’aide du jeu de donnĆ©es diamonds du package ggplot2.

library(ggplot2)
?diamonds
  • Quel est le code de la couleur la plus prisĆ©e ? D
  • Quel est le code de la moins bonne clartĆ© ? I1
  • ƀ quoi correspond la variable z ? La profondeur des diamands en millimĆØtres
  • En quoi la variable depth est-elle diffĆ©rente de la variable z ? Il s’agit d’un ratio entre z et la moyenne des 2 autres dimensions du diamands

Consultez l’aide du package nycflights13 en tapant help(package=ā€œnycflights13ā€).

help(package="nycflights13")

Consultez l’aide des 5 jeux de donnĆ©es de ce package.

?airlines
?airports
?flights
?planes
?weather
  • ƀ quoi correspond la variable visib ? VisibilitĆ© en miles
  • Dans quel tableau se trouve-t’elle ? weather
  • Combien de lignes possĆØde ce tableau ?
# Nombre de lignes du tableau weather
nrow(weather)
[1] 26115

Chapitre 4

library(tidyverse)
library(nycflights13)

Exercices 4.3

alaska_flights contient les mĆŖmes variables que flights, mais beaucoup moins de lignes : seulement 714.

alaska_flights <- flights %>%
  filter(carrier == "AS")

alaska_flights

Exercices 4.3.3

1. Donnez une raison pratique expliquant pourquoi les variables dep_delay et arr_delay ont une relation positive.

Le retard au dĆ©part se rĆ©percute forcĆ©ment sur le retard Ć  l’arrivĆ©e. Sauf s’il est possible pour un avion de rattraper son retard en vol, ce qui semble peu probable.

2. Quelles variables (pas nƩcessairement dans le tableau alaska_flights) pourraient avoir une corrƩlation nƩgative (relation nƩgative) avec dep_delay ? Pourquoi ? Rappelez-vous que nous Ʃtudions ici des variables numƩriques.

On peut supposer que la température, la visibilité et le rayonnement solaire sont corrélés négativement à dep_delay. Quand les conditions météos se dégradent (moins de soleil, température plus faible, plus de brouillard donc moins de visibilité) les retards augmentent car les avions et les pistes doivent être (re)mis en état plus souvent. Cela demande plus de maintenance.

3. Selon vous, pourquoi tant de points sont-il regroupés près de (0, 0) ?

La majoritĆ© des vols dĆ©collent et aterrisent Ć  l’heure ou avec peu d’avance ou de retard

4. ƀ quoi le point (0,0) correspond-il pour les vols d’Alaska Airlines ?

Cela correspond Ć  la situation idĆ©ale de dĆ©collage et d’aterrissage Ć  l’heure.

5. Citez les ƩlƩments de ce graphique/de ces donnƩes qui vous sautent le plus aux yeux ?

Il s’agit ici de rĆ©aliser l’exercice qui est demandĆ© dans la partie ā€œrĆ©sultatsā€ de tout rapport ou compte rendu. Ici, il y a 4 choses Ć  dĆ©crire :

  1. La relation entre les 2 variables est positive.
  2. La relation entre les 2 variables semble linƩaire.
  3. La grande majoritĆ© des vols est Ć  peu prĆØs Ć  l’heure, dans une fourchette de 25 minutes d’avance ou de retard, au dĆ©part comme Ć  l’arrivĆ©e.
  4. Il y a toutefois une forte variabilité avec des valeurs extrêmes très fortes : certains vols accumulent plus de 3h de retard.

On discute donc de la nature de la relation, de sa forme, de la tendance centrale (où sont situés la majorité des points) et des extrêmes.

6. CrĆ©ez un nouveau nuage de points en utilisant d’autres variables du jeu de donnĆ©es alaska_flights

Pour tenter de vérifier notre hypothèse sur le lien entre météo et retard :

ggplot(data = alaska_flights, aes(x = month, y = arr_delay)) +
  geom_point()

Exercices 4.3.6

L’argument stroke permet de spĆ©cifier l’épaisseur des traits utilisĆ©s pour tracer les symboles (cercles, carrĆ©s, etc.) sur un graphique.

CrƩation du jeu de donnƩes diams :

set.seed(4532) # Afin que tout le monde récupère les mêmes lignes
diams <- diamonds %>%
  sample_n(5000)

CrƩation du graphique :

ggplot(diams, aes(x = carat, y = price, color = clarity)) +
  geom_point(shape = 4, alpha = 0.6)

Revenir sur la position des arguments esthƩtiques : dans ggplot() ou dans geom_XXX() ?

  • ƀ quoi sont dues les bandes verticales ?*

Les bandes verticales sont le rĆ©sultat d’approximations humaines. On a tendance Ć  arrondir au carat ā€œrondā€ immĆ©diatement supĆ©rieur (2 au lieu de 1.98, 1.7 au lieu de 1.69, etc), car la valeur marchande dĆ©pend du carat.

Exercices 4.4.2

CrƩation du jeu de donnƩes small_weather :

small_weather <- weather %>%
  filter(origin == "EWR",
         month == 1,
         day <= 15)

small_weather

Expliquez pourquoi la variable time_hour identifie de maniĆØre unique le moment ou chaque mesure a Ć©tĆ© rĆ©alisĆ©e alors que ce n’est pas le cas de la variable hour.

hour est une variable entiĆØre qui prend les valeurs 0 Ć  23 chaque jour de l’annĆ©e. Indiquer qu’une mesure a Ć©tĆ© faite Ć  13h n’est pas suffisant car elle a pu ĆŖtre faite n’importe que jour. La variable time-hour contient la date et l’heure de chaque mesure, ce qui en fait un identifiant unique de chaque mesure.

Exercices 4.6.3

Examinez la figure 4.37.

1. Quels Ć©lĆ©ments nouveaux ce graphiques nous apprend-il par rapport au graphique 4.34 ci-dessus ? Comment le ā€œfacetingā€ nous aide-t’il Ć  visualiser les relations entre 2 (ou 3) variables ?

La nature bimodale de la distribution de la premiĆØre figure cache en fait une distribution mensuelle des tempĆ©ratures unimodale tout Ć  fait classique, et ce, pour les 3 aĆ©roports. Ce graphique nous permet Ć©galement de visualiser la variabilitĆ© mensuelle, annuelle, et entre les aĆ©roports ce qui Ć©tait impossible avec l’histogramme synthĆ©tique de la figure 4.34.

  • Les comparaisons en ligne nous permettent de comparer les tempĆ©rature pour un mois donnĆ© entre les 3 aĆ©roports. Nous constatons ici que ces tempĆ©ratures sont toujours trĆØs similaires. Les distributions des tempĆ©ratures dans les 3 aĆ©roports de NY sont trĆØs proches. Ce qui est finalement tout Ć  fait normal compte tenu de leur proximitĆ© gĆ©ographique.
  • Les comparaisons en colonnes nous permettent de visualiser l’évolution mensuelle des tempĆ©ratures pour un aĆ©roport donnĆ©. Ici, on constate que l’amplitude varie, de mĆŖme que la position des pics : il fait plus chaud l’étĆ© que l’hiver. LĆ  encore, c’est logique, nous sommes dans l’hĆ©misphĆØre Nord.

2. ƀ quoi correspondent les numĆ©ros 1 Ć  12 ?

Aux mois de l’annĆ©e

3. ƀ quoi correspondent les chiffres 25, 50, 75, 100 ?

Aux tempƩratures en degrƩs farenheit

4. ƀ quoi correspondent les chiffres 0, 100, 200, 300 ?

Au nombre d’observations pour chaque catĆ©gorie

5. Observez les Ć©chelles des axes x et y pour chaque sous graphique. Qu’on-t’elles de particulier ? En quoi est-ce utile ?

Elles sont toutes identiques. Elles facilitent ainsi les comparaisons entre sous-graphiques. Nous verrons peut-ĆŖtre plus tard que ce n’est pas toujours souhaitable.

6. La variabilitƩ des tempƩratures est-elle plus importante entre les aƩroports, entre les mois, ou au sein des mois ? Expliquez votre rƩflexion.

La variabilitĆ© des tempĆ©ratures entre aĆ©roports est trĆØs faible : pour un mois donnĆ©, les tempĆ©ratures observĆ©es sont distribuĆ©es de faƧon trĆØs similaire dans les 3 aĆ©roports. La variabilitĆ© entre les mois est plus importante : au sein d’un mois, la distribution des tempĆ©ratures couvre rarement plus de 30Āŗ Farenheit alors qu’elle atteint presque 50Āŗ Farenheit entre les mois d’étĆ© et les mois d’hiver.

Exercice 4.8.3

1. Quelle est la différence entre un histogramme et un diagramme bâtons ?

  • Un histogramme permet de visualiser la distribution d’une variable continue.
  • Un diagramme bĆ¢tons permet de visualiser la distribution d’une variable catĆ©gorielle

2. Pourquoi les histogrammes sont-ils inadaptƩs pour visualiser des donnƩes catƩgorielles ?

Parce que les barres sont collées les unes aux autres ce qui donne une impression visuelle de continuité. Par ailleurs, les barres ne peuvent pas être ré-ordonnées, ce qui est souvent nécessaire pour une variable catégorielle.

3. Quel est le nom de la companie pour laquelle le plus grand nombre de vols ont quitté New York en 2013 (je veux connaître son nom, pas juste son code) ? Où se trouve cette information ?

On examine la figure 4.47 (par exemple) et on constate que la compagnie en question a le code UA. Le nom complet de cette compagnie se trouve dans le tableau airlines. Ce tableau fait le lien entre codes et nom des compagnies aƩriennes.

La compagnie qui a affrƩtƩ le plus grand nombre de vols au dƩpart de New York en 2013 est United Air Lines Inc.

airlines

4. Quel est le nom de la companie pour laquelle le plus petit nombre de vols ont quitté New York en 2013 (je veux connaître son nom, pas juste son code) ? Où se trouve cette information ?

MĆŖme dĆ©marche : il s’agit de la compagnie Skywest Airlines Inc.Ā (code OO).

Exercices 4.8.4

La figure 4.47 et la tableau carrier_table permettent de rƩpondre aux questions suivantes

Comparez les compagnies ExpressJet Airlines (EV) et US Airways (US). De combien de fois la part de EV est-elle supĆ©rieure Ć  celle d’US ? (2 fois, 3 fois, 1.2 fois ?…)

Un peu plus de 2,5

Quelle est la troisième compagnie aérienne la plus importante en terme de nombre de vols au départ de New York en 2013 ?

ExpressJet Airlines (EV)

Combien de companies aƩriennes ont moins de vols que United Airlines (UA) ?

Toutes, soit 15 au total

Exercices 4.10

CrƩation du jeu de donnƩes :

# On fixe le générateur de nombres aléatoires pour choisir les mêmes lignes que dans l'exemple du livre
set.seed(1234)

# CrƩation de small_flights
small_flights <- flights %>%
  sample_n(1000) %>%
  filter(!is.na(arr_delay),
         distance < 3000)

small_flights

CrƩation du premier graphique

ggplot(small_flights, aes(x = distance, 
                          y = air_time,
                          color = origin,
                          shape = origin)) +
  geom_point(alpha = 0.8) +
  scale_color_brewer(palette = "Set1") +
  labs(x = "Distance parcourue en vol (miles)",
       y = "Temps de vol (minutes)",
       title = "Relation entre le temps de vol et la distance parcourue",
       subtitle = "Seuls les vols au dƩpart de JFK et Newark parcourent plus de 1600 miles",
       color = "AƩroport de\nNew York",
       shape = "AƩroport de\nNew York",
       caption = "DonnƩes : small_flights") +
  theme_bw()

CrƩation du second graphique

ggplot(small_flights, aes(x = factor(month), fill = origin)) +
  geom_bar() +
  facet_wrap(~ fct_infreq(carrier), nrow = 4) +
  scale_fill_brewer(palette = "Accent") +
  labs(x = "Mois de l'annƩe 2013",
       y = "Nombre de vols",
       title = "Ɖvolution mensuelle du trafic aĆ©rien New Yorkais en 2013",
       subtitle = "Seules 9 compagnies aƩriennes sur 14 ont dƩservi New York toute l'annƩe",
       fill = "AƩroport de\nNew York",
       caption = "DonnƩes : small_flights") +
  theme_bw()

small_flights

Chapitre 5

Exercice 5.2.4

Examinez les tableaux rates, storms et population du package EDAWR.

  • Ces tableaux sont-ils des ā€œtableaux rangĆ©sā€ (tidy data) ?
  • Si oui, quelles sont les variables reprĆ©sentĆ©es ?
  • Si non, transformez-les en ā€œtableaux rangĆ©sā€.
# Installation et mise en mƩmoire des packages nƩcessaires
library(tidyverse)

# install.packages("devtools")
# library(devtools)

# install_github("rstudio/EDAWR")
library(EDAWR)

Affichage des tableaux :

rates

rates est un tableau rangƩ avec une variable par colonne (pays, annƩe, nombre de cas de tuberculose, population globale, et taux de malades) et une ligne par observation.

storms

storms est un tableau rangé avec une variable par colonne (nom de la tempête, vitesse du vent en mph, pression en millibars et date à laquelle la plus forte vitesse de vent a été enregistrée) et une ligne par observation.

population

population n’est pas un tableau rangĆ©. Les variables devraient ĆŖtre pays, annĆ©e et population

pivot_longer(population, col = `1995`:`2013`, names_to = "year", values_to = "pop")

Exercice 5.3.4

1. L’objet dauphin est-il ā€œtidyā€ (autrement dit, s’agit-il de ā€œdonnĆ©es rangĆ©esā€) ? Justifiez.

# Importation du jeu de donnƩes `dauphin` :
library(readxl)
dauphin <- read_excel("dauphin.xls", na = "*", skip = 9)
names(dauphin) <- c("ID", "Sexe", "Statut", "Taille", "Age", "Cd", "Cu", "Hg", "Organe")
dauphin

Oui, dauphin est ā€œtidyā€. Il contient une variable par colonne, une observation par ligne. La variable ID pourrait toutefois ĆŖtre supprimĆ©e.

2. Produisez le graphique ci-dessous

ggplot(dauphin, aes(x = Age, y = Hg, color = Sexe)) +
  geom_smooth(method = "lm") +
  geom_point() +
  facet_wrap(~ Organe, nrow = 2, scales = "free_y") +
  labs(x = "Ƃge (annĆ©es)",
       y = "Concentration en Mercure (mg/kg)",
       title = "Ɖvolution de la concentration en mercure age l'Ć¢ge chez Delphinus delphis",
       color = "Sexe",
       caption = "DonnƩes : dauphin.xls") +
  theme_bw()
`geom_smooth()` using formula 'y ~ x'

3. Importez dans R le jeu de donnĆ©es whoTB.csv. Ce jeu de donnĆ©es contient les cas de tuberculose (TB) rapportĆ©s par l’Organisation Mondiale de la SantĆ© (OMS, ou WHO en anglais : World Health Organization). Les cas sont rĆ©pertoriĆ©s par annĆ©e, pays, Ć¢ge, sexe, type de tuberculose et mĆ©thode de diagnostique. Selon vous, ce jeu de donnĆ©es est-il ā€œrangĆ©ā€ ? Pourquoi ?

library(readr)
whoTB <- read_csv("whoTB.csv")

── Column specification ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
cols(
  .default = col_double(),
  country = col_character(),
  iso2 = col_character(),
  iso3 = col_character()
)
ℹ Use `spec()` for the full column specifications.
whoTB

Ce jeu de donnĆ©es n’est pas rangĆ© car outre les 4 premiĆØres colonnes, toutes les autres contiennent de multiples informations : nouveaux cas ou anciens (ici, ils sont tous nouveaux), type de cas (sp, sn, ep ou rel), sexe des patients (m ou f) et enfin tranches d’âges. Nous devrions donc avoir ces 4 variables supplĆ©mentaires, avec pour chaque combinaison possible, le nombre de cas de tuberculose rapportĆ©e dans les colonnes.

4. Si ce jeu de donnĆ©es n’est pas rangĆ©, rangez-le en utilisant les fonctions du packages tidyr que nous avons dĆ©couvertes dans ce chapitre : pivot_longer(), pivot_wider(), separate() et unite() (vous n’aurez pas nĆ©cessairement besoin d’utiliser ces 4 fonctions, et Ć  l’inverse, certaines devront peut-ĆŖtre ĆŖtre utilisĆ©es plusieurs fois).

On commence donc par rassembler toutes les colonnes en une seule, nommƩe composite et tous les chiffres dans une colonne cases :

whoTB %>% 
  pivot_longer(cols = `new_sp_m014`:`new_rel_f65`, names_to = "composite", values_to = "cases")

Puisqu’ils y a beaucoup de donnĆ©es manquantes, on les supprime. Cela nous permet de passer de plus de 400000 lignes Ć  environ 76000

whoTB %>% 
  pivot_longer(cols = `new_sp_m014`:`new_rel_f65`, names_to = "composite", 
               values_to = "cases", values_drop_na = TRUE)

Il nous faut ensuite sƩparer la colonne composite en plusieurs colonnes. On va se servir des _ pour isoler new, le type de cas, et la combinaison SexeAge :

whoTB %>% 
  pivot_longer(cols = `new_sp_m014`:`new_rel_f65`, names_to = "composite", 
               values_to = "cases", values_drop_na = TRUE) %>% 
  separate(col = composite, into = c("new", "type", "SexeAge"))

Nous pouvons enfin séparer la colonne SexeAge en 2 en plaçant le premier caractère dans une colonne sexe et le reste dans une colonne age_class :

whoTB %>% 
  pivot_longer(cols = `new_sp_m014`:`new_rel_f65`, names_to = "composite", 
               values_to = "cases", values_drop_na = TRUE) %>% 
  separate(col = composite, into = c("new", "type", "SexeAge")) %>% 
  separate(col = SexeAge, into = c("sexe", "age_class"), sep = 1)

Pour faire bonne mesure, on peut supprimer les colonnes iso2 et iso3 car elles sont redondantes avec country, ainsi que new qui ne contient aucune information. On stocke enfin le rƩsultat dans un tableau nommƩ tidyTB :

tidyTB <- whoTB %>% 
  pivot_longer(cols = `new_sp_m014`:`new_rel_f65`, names_to = "composite", 
               values_to = "cases", values_drop_na = TRUE) %>% 
  separate(col = composite, into = c("new", "type", "SexeAge")) %>% 
  separate(col = SexeAge, into = c("sexe", "age_class"), sep = 1) %>% 
  select(-iso2, -iso3, -new)

tidyTB

Il est maintenant facile de faire des graphiques exploratoire. Par exemple :

tidyTB %>% 
  filter(country == "France") %>% 
  ggplot(aes(x = year, y = cases, color = sexe)) +
    geom_line() +
    facet_wrap(~age_class, ncol = 2) +
    labs(title = "Nouveaux cas de tuberculose en France, de 1995 Ć  2013",
         caption = "DonnƩes : OMS")


Chapitre 6

Mise en mƩmoire des packages utiles

library(dplyr)
library(ggplot2)
library(nycflights13)

Exercice 6.4.2

Dans la section 3.3.1, nous avons utilisĆ© la fonction View et l’application manuelle de filtres pour dĆ©terminer combien de vols avaient quittĆ© l’aĆ©roport JFK le 12 fĆ©vrier 2013. En utilisant la fonction filter(), crĆ©ez un objet nommĆ© JFK_12fev qui contiendra les donnĆ©es de ces vols. VĆ©rifiez que cet objet contient bien 282 lignes.

JFK_12fev <- flights %>% 
  filter(origin == "JFK",
         month == 2,
         day == 12)

JFK_12fev
nrow(JFK_12fev)
[1] 282

Exercice 6.4.3

Selon vous, quelles raisons peuvent expliquer qu’un vol qui a dĆ©collĆ© n’ait pas d’heure d’atterrissage ?

Le vol s’est Ć©crasĆ© ? Plus vraisemblablement, le vol a dĆ» faire demi-tour ou ĆŖtre dĆ©routĆ© suite Ć  un problĆØme technique (ou mĆ©tĆ©o ?).

Exercice 6.5.6

1. Faites un tableau indiquant combien de vols ont été annulés après le décollage, pour chaque compagnie aérienne.

flights %>%                     # On prend flights, puis
  filter(!is.na(dep_time),      # On filtre les vols ayant une heure de dƩcollage
         is.na(arr_time)) %>%   # Mais pas d'heure d'arrivƩe, puis
  group_by(carrier) %>%         # On groupe par compagnie aƩrienne, puis
  summarize(cancelled = n())    # On compte le nombre de lignes par groupe

2. Faites un tableau indiquant les vitesses de vents minimales, maximales et moyennes, enregistrƩes chaque mois dans chaque aƩroport de New York.

weather %>% 
  group_by(origin, month) %>% 
  summarize(max_wind = max(wind_speed, na.rm = TRUE),
            min_wind = min(wind_speed, na.rm = TRUE),
            moy_wind = mean(wind_speed, na.rm = TRUE))
`summarise()` has grouped output by 'origin'. You can override using the `.groups` argument.

3. Sachant que les vitesses du vent sont exprimĆ©es en miles par heure, certaines valeurs sont-elles surprenantes ? ƀ l’aide de la fonction filter(), Ć©liminez la ou les valeurs aberrantes.

Une valeur de plus de 1000 mph est impossible. Il s’agit vraisemblablement d’une erreur de saisie ou d’enregistrement.

weather %>%
  filter(wind_speed <= 500) %>% 
  group_by(origin, month) %>% 
  summarise(max_wind = max(wind_speed, na.rm = TRUE),
            min_wind = min(wind_speed, na.rm = TRUE),
            moy_wind = mean(wind_speed, na.rm = TRUE))
`summarise()` has grouped output by 'origin'. You can override using the `.groups` argument.

4. En utilisant les donnƩes de vitesse de vent du tableau weather, produisez le graphique suivant.

weather %>% 
  filter(wind_speed < 500) %>% 
  ggplot(aes(x = factor(month), y = wind_speed)) +
  geom_jitter(width = 0.2, height = 0, alpha = 0.2) +
  labs(x = "Mois",
       y = "Vitesse du vent (mph)")

Selon vous, pourquoi les points sont-ils organisĆ©s en bandes horizontales ? Selon vous, pourquoi n’y a t’il jamais de vent entre 0 et environ 3 miles Ć  l’heure (mph) ?

L’appareil de mesures n’enregistre que des vitesses de vent par incrĆ©ment fixe et son seuil de dĆ©tection est supĆ©rieur Ć  des vitesses de 3mph. Cela explique les bandes et le trou entre 0 et 3 mph.

Sachant qu’en divisant des mph par 1.151 on obtient des vitesses en nœuds, que nous apprend cette commande :

sort(unique(weather$wind_speed))/1.151
 [1]   0.000000   2.999427   3.999235   4.999044   5.998853   6.998662   7.998471   8.998280   9.998089  10.997897  11.997706  12.997515  13.997324  14.997133  15.996942  16.996751  17.996560  18.996368  19.996177  20.995986  21.995795  22.995604  23.995413  24.995222  25.995030  26.994839  27.994648  28.994457  29.994266  30.994075  31.993884  32.993692  33.993501  34.993310  36.992928 910.825873

Ici, on convertit les donnĆ©es de vitesse de vent de mph en nœuds. La fonction unique() permet d’éliminer les duplicats et sort() trie les donnĆ©es en ordre croissant. On voit donc que seules quelques valeurs de vent sont enregistrĆ©es. L’enregistreur est inacpable de dĆ©tecter moins de 3 nœuds de vent, et ensuite, il enregistre des valeurs entiĆØres (3, 4, 5, et ainsi de suite jusqu’au maximum de 37 nœuds).

Exercice 6.7.2

1. Dans ggplot2 le jeu de donnĆ©es mpg contient des informations sur 234 modĆØles de voitures. Examinez ce jeu de donnĆ©es avec la fonction View() et consultez l’aide de ce jeu de donnĆ©es pour savoir Ć  quoi correspondent les diffĆ©rentes variables. Quelle(s) variable(s) nous renseignent sur la consommation des vĆ©hicules ? ƀ quoi correspond la variable disp ?

Les variables hwy et cty nous renseignent sur la consommation des vƩhicules sur autoroute et en ville respectivement. Les consommations sont donnƩes en miles per galon. disp est la cylindrƩe du moteur, son volume en litres.

2. La consommation est donnée en miles par gallon. Créez une nouvelle variable conso qui contiendra la consommation exprimée en nombre de litres pour 100 kilomètres.

mpg %>% 
  mutate(conso = 235.215 / hwy)

3. FaĆ®tes un graphique prĆ©sentant la relation entre la cylindrĆ©e en litres et la consommation sur autoroute exprimĆ©e en nombre de litres pour 100 kilomĆØtres. Vous excluerez les vĆ©hicules dont la classe est 2seater de ce graphique (il s’agit de voitures de sports trĆØs compactes qu’il est difficile de mesurer aux autres). Sur votre graphique, la couleur devrait reprĆ©senter le type de vĆ©hicule. Vous ajouterez une droite de rĆ©gression en utilisant geom_smooth(method = "lm").

mpg %>% 
  filter(class != "2seater") %>% 
  mutate(conso = 235.215 / hwy) %>% 
  ggplot(aes(x = displ, y = conso)) +
  geom_point(aes(color = class)) +
  geom_smooth(method = "lm") +
  labs(x = "CylindrƩe (volume du moteur en litres)",
       y = "Consommation (litres pour 100 kilomĆØtres)",
       color = "Type de\nvƩhicule",
       title = "Relation positive entre cylindrƩe et consommation") +
  theme_minimal(base_family = "Gill Sans")
`geom_smooth()` using formula 'y ~ x'

*4. Ce graphique prĆ©sente-t’il correctement l’ensemble des donnĆ©es de ces 2 variables ? Pourquoi ? Comparez le graphique de la question 3 ci-dessus et le graphique prĆ©sentĆ© ci-dessous. Selon vous, quels arguments et/ou fonctions ont Ć©tĆ© modifiĆ©s pour arriver Ć  ce nouveau graphique ? Quels sont les avantages et les inconvĆ©nients de ce graphique par rapport au prĆ©cĆ©dent ?

mpg %>% 
  filter(class != "2seater") %>% 
  mutate(conso = 235.215 / hwy) %>% 
  ggplot(aes(x = displ, y = conso)) +
  geom_jitter(aes(fill = class), shape = 21, width = 0.05, height = 0.05, alpha = 0.7) +
  geom_smooth(method = "lm") +
  labs(x = "CylindrƩe (volume du moteur en litres)",
       y = "Consommation (litres pour 100 kilomĆØtres)",
       fill = "Type de\nvƩhicule",
       title = "Relation positive entre cylindrƩe et consommation") +
  theme_minimal(base_family = "Gill Sans")
`geom_smooth()` using formula 'y ~ x'

On utilise geom_jitter() au lieu de geom_point() pour que les points ne se superposent plus. On voit donc mieux les données. Néanmoins, les points sont légèrement déplacés, ce graphique est donc moins précis que le premier. Il est plus inexact.

Les symboles ont aussi Ć©tĆ© changĆ©s : on est passĆ© Ć  des points avec contour (shape = 21) auquels on a attribuĆ© une transparence (toujours pour mieux visualiser les chevauchements de points). Du coup, ce n’est plus la color qui est associĆ©e Ć  la classe de vĆ©hicule, mais fill, la couleur de remplissage.

Exercice 6.10

1. CrĆ©ez un tableau delayed indiquant, pour chaque compagnie aĆ©rienne et chaque mois de l’annĆ©e, le nombre de vols ayant eu un retard supĆ©rieur Ć  30 minutes Ć  l’arrivĆ©e Ć  destination. Ce tableau devrait contenir uniquement 3 colonnes :

  • carrier : la compagnie aĆ©rienne
  • month : le mois de l’annĆ©e 2013
  • n_delayed : le nombre de vols ayant plus de 30 minutes de retard
# Calcul du nombre de vols en retard (+ de 30 min Ơ l'arrivƩe) pour chaque compagnie et chaque mois
delayed <- flights %>%
  filter(arr_delay > 30) %>%
  group_by(carrier, month) %>%
  summarize(n_delayed = n(), .groups = "drop")

delayed

2. CrĆ©ez un tableau total indiquant le nombre total de vols affrĆ©tĆ©s (et non annulĆ©s) par chaque compagnie aĆ©rienne et chaque mois de l’annĆ©e. Ce tableau devrait contenir seulement 3 colonnes :

  • carrier : la compagnie aĆ©rienne
  • month : le mois de l’annĆ©e 2013
  • n_total : le nombre total de vols arrivĆ©s Ć  destination
# Calcul du nombre total de vols non annulƩs pour chaque compagnie et chaque mois
total <- flights %>%
  filter(!is.na(arr_delay)) %>%
  group_by(carrier, month) %>%
  summarize(n_total = n())
`summarise()` has grouped output by 'carrier'. You can override using the `.groups` argument.
total

3. Fusionnez ces 2 tableaux en rĆ©alisant la jointure appropriĆ©e. Le tableau final, que vous nommerez carrier_stats devrait contenir 185 lignes. Si certaines colonnes contiennent des donnĆ©es manquantes, remplacez-les par des 0 Ć  l’aide des fonctions mutate() et na_replace().

4. Ajoutez Ć  votre tableau carrier_stats une variable rate qui contient la proportion de vols arrivĆ©s Ć  destination avec plus de 30 minutes de retard, pour chaque compagnie aĆ©rienne et chaque mois de l’annĆ©e.

5. Ajoutez Ơ votre tableau carrier_stats le nom complet des compagnies aƩriennes en rƩalisant la jointure appropriƩe avec le tableau airlines.

# Création du tableau de synthèse (2 left_join())
carrier_stats <- total %>%
  left_join(delayed) %>%
  mutate(n_delayed = replace_na(n_delayed, 0),
         rate = n_delayed / n_total) %>%
  left_join(airlines)
Joining, by = c("carrier", "month")
Joining, by = "carrier"
carrier_stats

6. Faites un graphique synthƩtique prƩsentant ces rƩsultats de la faƧon la plus claire possible

7. Quelle compagnie aĆ©rienne semble se comporter trĆØs diffĆ©remment des autres ? ƀ quoi pouvez-vous attribuer ce comportement atypique ?

La compagnie OO (SkyWest Airlines Inc.) a un comportement trĆØs atypique dĆ» au trĆØs faible nombre de vols affrĆ©tĆ©s (1 seul en janvier, 2 en juin, 4 en aoĆ»t, 17 en septembre et 5 en novembre). Elle n’est d’ailleurs prĆ©sente que quelques mois de l’annĆ©e dans les aĆ©roports de New York.

carrier_stats %>%
  filter(carrier == "OO")

8. Pour les compagnies affrétant un grand nombre de vols chaque année (e.g. UA, B6 et EV), quelles sont les périodes où les plus fortes proportions de vols en retard sont observées ? Et les plus faibles ? Quelle(s) hypothèse(s) pouvez-vous formuler pour expliquer ces observations ?

Pour les plus grosses compagnies, les retards les plus frĆ©quents sont observĆ©s l’étĆ©, et les moins frĆ©quents en automne. Cela correspond aux pĆ©riodes de trĆØs fortes affluences en Ć©tĆ©, et au moins fortes affluences Ć  la rentrĆ©e de septembre. C’est du moins le cas pour UA et B6. Moins pour EV.

carrier_stats %>%
  filter(carrier %in% c("UA", "B6", "EV")) %>%
  select(carrier, month, n_total) %>%
  pivot_wider(names_from = carrier, 
              values_from = n_total)
carrier_stats %>%
  filter(carrier %in% c("UA", "B6", "EV")) %>%
  ggplot(aes(x = factor(month), y = n_total, group = carrier, color = carrier)) +
    geom_line()

9. Faites un tableau synthƩtique prƩsentant ces rƩsultats de la faƧon la plus compacte et claire que possible, afin par exemple de les intƩgrer Ơ un rapport.

LS0tCnRpdGxlOiAiQ29ycmVjdGlvbiBkZXMgZXhlcmNpY2VzIgphdXRob3I6ICJCZW5vw650IFNpbW9uLUJvdWhldCIKZGF0ZTogIlZlbmRyZWRpIDI5IG9jdG9icmUgMjAyMSIKb3V0cHV0OgogIHBkZl9kb2N1bWVudDoKICAgIGRmX3ByaW50OiBkZWZhdWx0CiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIHRoZW1lOiBjb3NtbwogICAgaGlnaGxpZ2h0OiBrYXRlCiAgICBjZW50ZXI6IHllcwogIGh0bWxfZG9jdW1lbnQ6CiAgICBkZl9wcmludDogZGVmYXVsdAogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCiAgICB0aGVtZTogY29zbW8KICAgIGhpZ2hsaWdodDoga2F0ZQogICAgY2VudGVyOiB5ZXMKICBodG1sX25vdGVib29rOgogICAgZGZfcHJpbnQ6IGRlZmF1bHQKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwogICAgdGhlbWU6IGNvc21vCiAgICBoaWdobGlnaHQ6IGthdGUKICAgIGNlbnRlcjogeWVzCi0tLQoKIyBDaGFwaXRyZSAyCgojIyBFeGVyY2ljZSAyLjQKCkluc3RhbGxhdGlvbiBldCBjaGFyZ2VtZW50IGRlIGBnZ3Bsb3QyYCA6CgpgYGB7ciwgdGlkeT1GQUxTRX0KIyBJbnN0YWxsYXRpb24gZHUgcGFja2FnZSBnZ3Bsb3QyCiMgaW5zdGFsbC5wYWNrYWdlcyhnZ3Bsb3QyKQoKIyBNaXNlIGVuIG3DqW1vaXJlIGR1IHBhY2thZ2UKbGlicmFyeShnZ3Bsb3QyKQpgYGAKCk1pc2UgZW4gbcOpbW9pcmUgZHUgamV1IGRlIGRvbm7DqWVzIGBkaWFtb25kc2AgOgoKYGBge3J9CiMgQ2hhcmdlbWVudCBkdSBqZXUgZGUgZG9ubsOpZXMgZGlhbW9uZHMKZGF0YShkaWFtb25kcykKYGBgCgpEw6l0ZXJtaW5hdGlvbiBkdSBub21icmUgZGUgbGlnbmVzIGRlIGNlIHRhYmxlYXUgOgoKYGBge3J9CiMgTm9tYnJlIGRlIGxpZ25lcyBkZSBkaWFtb25kcwpkaWFtb25kcwpucm93KGRpYW1vbmRzKQpzdHIoZGlhbW9uZHMpCmBgYAoKQ3LDqWF0aW9uIGRlIGBkaWFtYW50c19jaGVyc2AgY29udGVuYW50IHVuaXF1ZW1lbnQgbGVzIGluZm9zIGRlcyBkaWFtYW50cyBkZSBwbHVzIGRlIDE1MDAwIFVTRCA6CgpgYGB7ciwgdGlkeT1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KIyBQcmVtacOocmUgbcOpdGhvZGUgOgpkaWFtYW50c19jaGVycyA8LSBkaWFtb25kc1tkaWFtb25kcyRwcmljZSA+IDE1MDAwLCBdCgojIERldXhpw6htZSBtw6l0aG9kZSA6CmRpYW1hbnRzX2NoZXJzIDwtIHN1YnNldChkaWFtb25kcywgZGlhbW9uZHMkcHJpY2UgPiAxNTAwMCkKCiMgVHJvaXNpw6htZSBtw6l0aG9kZQpsaWJyYXJ5KGRwbHlyKQpkaWFtYW50c19jaGVycyA8LSBkaWFtb25kcyAlPiUKICBmaWx0ZXIocHJpY2UgPiAxNTAwMCkKCmRpYW1hbnRzX2NoZXJzCmBgYAoKTm9tYnJlIGRlIGRpYW1hbnRzIGRvbnQgbGUgcHJpeCBkw6lwYXNzZSAxNTAwMCBVU0QgOgpgYGB7cn0Kc3VtKGRpYW1vbmRzJHByaWNlID4gMTUwMDApCmBgYAoKUHJvcG9ydGlvbiBkZSBkaWFtYW50cyBkb250IGxlIHByaXggZMOpcGFzc2UgMTUwMDAgVVNECmBgYHtyfQptZWFuKGRpYW1vbmRzJHByaWNlID4gMTUwMDApCmBgYAoKVHJpIGR1IHRhYmxlYXUgcGFyIG9yZHJlIGRlIHByaXggZMOpY3JvaXNzYW50cwpgYGB7ciwgdGlkeT1GQUxTRX0KIyMgUHJlbWnDqHJlIG3DqXRob2RlCmRpYW1hbnRzX2NoZXJzX3RyaSA8LSBkaWFtYW50c19jaGVyc1tvcmRlcihkaWFtYW50c19jaGVycyRwcmljZSwgZGVjcmVhc2luZyA9IFRSVUUpLCBdCgojIyBTZWNvbmRlIG3DqXRob2RlCmRpYW1hbnRzX2NoZXJzX3RyaSA8LSBkaWFtYW50c19jaGVycyAlPiUKICBhcnJhbmdlKGRlc2MocHJpY2UpKQpgYGAKCkFmZmljaGFnZSBkZXMgMjAgZGlhbWFudHMgbGVzIHBsdXMgY2hlcnMKYGBge3J9CmhlYWQoZGlhbWFudHNfY2hlcnNfdHJpLCBuID0gMjApCmBgYAoKYGBge3J9CiMgVW5lIGF1dHJlIGZhw6dvbiBkZSBmYWlyZSB0b3V0IMOnYSBhdmVjIGxlIHBpcGUgZXQgbGVzIGZvbmN0aW9ucyBkZSBkcGx5cgpsaWJyYXJ5KGRwbHlyKQpkaWFtb25kcyAlPiUgCiAgZmlsdGVyKHByaWNlID49IDE1MDAwKSAlPiUgCiAgYXJyYW5nZShkZXNjKHByaWNlKSkgJT4lIAogIHByaW50KG4gPSAyMCkKYGBgCgoKLS0tLQoKIyBDaGFwaXRyZSAzCgpgYGB7cn0KIyBNaXNlIGVuIG3DqW1vaXJlIGRlcyBwYWNrYWdlcyB1dGlsZXMKbGlicmFyeShkcGx5cikKbGlicmFyeShueWNmbGlnaHRzMTMpCmBgYAoKYGBge3IsIGV2YWw9RkFMU0V9CiMgQWZmaWNoYWdlIGR1IHRhYmxlYXUgZmxpZ2h0cyBkYW5zIHVuIG9uZ2xldCBkZSBSU3R1ZGlvClZpZXcoZmxpZ2h0cykKYGBgCgojIyBFeGVyY2ljZXMgMy4zLjEKCi0gQ2hhY3VuZSBkZXMgbGlnbmVzIGR1IHRhYmxlYXUgY29ycnNwb25kIGF1eCBkb25uw6llcyBkJ3VuIHZvbCAocsOpcG9uc2UgQikKLSBOb21icmUgZGUgdm9scyBheWFudCBkw6ljb2xsw6kgZGUgSkZLIGxlIDEyIGbDqXZyaWVyIDIwMTMgOiAyODIuIE9uIGxlIGTDqXRlcm1pbmUgZW4gYWp1c3RhbnQgbGVzIHNsaWRlcnMsIGxlcyBmaWx0cmVzLCBldGMuCgojIyBFeGVyY2ljZXMgMy4zLjIKCkxlcyB2YXJpYWJsZXMgY2F0w6lnb3JpZWxsZXMgc29udCA6IAoKMS4gYHRhaWxudW1gIChudW3DqXJvIGQnaWRlbnRpZmljYXRpb24gZGUgbCdhdmlvbiksIAoyLiBgb3JpZ2luYCAoYcOpcm9wb3J0IGRlIGTDqXBhcnQgZGUgbCdhdmlvbiksIAozLiBgZGVzdGAgKGHDqXJvcG9ydCBkZSBkZXN0aW5hdGlvbiBkZSBsJ2F2aW9uKSwKNC4gYGNhcnJpZXJgIChjb21wYWduaWUgYcOpcmllbm5lKSAKCkxlcyB2YWxldXJzIHNvbnQgZGVzIGNoYcOubmVzIGRlIGNhcmFjdMOocmVzIGV0IG5vbiBkZXMgY2hpZmZyZXMuCgojIyBFeGVyY2ljZXMgMy40CgpDb25zdWx0ZXogbOKAmWFpZGUgZHUgamV1IGRlIGRvbm7DqWVzIGRpYW1vbmRzIGR1IHBhY2thZ2UgZ2dwbG90Mi4KCmBgYHtyLCBldmFsPUZBTFNFfQpsaWJyYXJ5KGdncGxvdDIpCj9kaWFtb25kcwpgYGAKCi0gUXVlbCBlc3QgbGUgY29kZSBkZSBsYSBjb3VsZXVyIGxhIHBsdXMgcHJpc8OpZSA/IGBEYAotIFF1ZWwgZXN0IGxlIGNvZGUgZGUgbGEgbW9pbnMgYm9ubmUgY2xhcnTDqSA/IGBJMWAKLSDDgCBxdW9pIGNvcnJlc3BvbmQgbGEgdmFyaWFibGUgYHpgID8gTGEgcHJvZm9uZGV1ciBkZXMgZGlhbWFuZHMgZW4gbWlsbGltw6h0cmVzCi0gRW4gcXVvaSBsYSB2YXJpYWJsZSBgZGVwdGhgIGVzdC1lbGxlIGRpZmbDqXJlbnRlIGRlIGxhIHZhcmlhYmxlIGB6YCA/IElsIHMnYWdpdCBkJ3VuIHJhdGlvIGVudHJlIHogZXQgbGEgbW95ZW5uZSBkZXMgMiBhdXRyZXMgZGltZW5zaW9ucyBkdSBkaWFtYW5kcwoKCkNvbnN1bHRleiBs4oCZYWlkZSBkdSBwYWNrYWdlIG55Y2ZsaWdodHMxMyBlbiB0YXBhbnQgaGVscChwYWNrYWdlPSJueWNmbGlnaHRzMTMiKS4KCmBgYHtyLCBldmFsPUZBTFNFfQpoZWxwKHBhY2thZ2U9Im55Y2ZsaWdodHMxMyIpCmBgYAoKQ29uc3VsdGV6IGzigJlhaWRlIGRlcyA1IGpldXggZGUgZG9ubsOpZXMgZGUgY2UgcGFja2FnZS4KYGBge3IsIGV2YWwgPSBGQUxTRX0KP2FpcmxpbmVzCj9haXJwb3J0cwo/ZmxpZ2h0cwo/cGxhbmVzCj93ZWF0aGVyCmBgYAoKLSDDgCBxdW9pIGNvcnJlc3BvbmQgbGEgdmFyaWFibGUgYHZpc2liYCA/IFZpc2liaWxpdMOpIGVuIG1pbGVzCi0gRGFucyBxdWVsIHRhYmxlYXUgc2UgdHJvdXZlLXQnZWxsZSA/IGB3ZWF0aGVyYAotIENvbWJpZW4gZGUgbGlnbmVzIHBvc3PDqGRlIGNlIHRhYmxlYXUgPwoKYGBge3J9CiMgTm9tYnJlIGRlIGxpZ25lcyBkdSB0YWJsZWF1IHdlYXRoZXIKbnJvdyh3ZWF0aGVyKQpgYGAKCiMgQ2hhcGl0cmUgNAoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KG55Y2ZsaWdodHMxMykKYGBgCgoKIyMgRXhlcmNpY2VzIDQuMwoKYGFsYXNrYV9mbGlnaHRzYCBjb250aWVudCBsZXMgbcOqbWVzIHZhcmlhYmxlcyBxdWUgZmxpZ2h0cywgbWFpcyBiZWF1Y291cCBtb2lucyBkZSBsaWduZXMgOiBzZXVsZW1lbnQgNzE0LgpgYGB7ciwgdGlkeT1GQUxTRX0KYWxhc2thX2ZsaWdodHMgPC0gZmxpZ2h0cyAlPiUKICBmaWx0ZXIoY2FycmllciA9PSAiQVMiKQoKYWxhc2thX2ZsaWdodHMKYGBgCgoKIyMgRXhlcmNpY2VzIDQuMy4zCgoqMS4gRG9ubmV6IHVuZSByYWlzb24gcHJhdGlxdWUgZXhwbGlxdWFudCBwb3VycXVvaSBsZXMgdmFyaWFibGVzIGBkZXBfZGVsYXlgIGV0IGBhcnJfZGVsYXlgIG9udCB1bmUgcmVsYXRpb24gcG9zaXRpdmUuKgoKTGUgcmV0YXJkIGF1IGTDqXBhcnQgc2UgcsOpcGVyY3V0ZSBmb3Jjw6ltZW50IHN1ciBsZSByZXRhcmQgw6AgbCdhcnJpdsOpZS4gU2F1ZiBzJ2lsIGVzdCBwb3NzaWJsZSBwb3VyIHVuIGF2aW9uIGRlIHJhdHRyYXBlciBzb24gcmV0YXJkIGVuIHZvbCwgY2UgcXVpIHNlbWJsZSBwZXUgcHJvYmFibGUuCgoqMi4gUXVlbGxlcyB2YXJpYWJsZXMgKHBhcyBuw6ljZXNzYWlyZW1lbnQgZGFucyBsZSB0YWJsZWF1IGBhbGFza2FfZmxpZ2h0c2ApIHBvdXJyYWllbnQgYXZvaXIgdW5lIGNvcnLDqWxhdGlvbiBuw6lnYXRpdmUgKHJlbGF0aW9uIG7DqWdhdGl2ZSkgYXZlYyBgZGVwX2RlbGF5YCA/IFBvdXJxdW9pID8gUmFwcGVsZXotdm91cyBxdWUgbm91cyDDqXR1ZGlvbnMgaWNpIGRlcyB2YXJpYWJsZXMgbnVtw6lyaXF1ZXMuKgoKT24gcGV1dCBzdXBwb3NlciBxdWUgbGEgdGVtcMOpcmF0dXJlLCBsYSB2aXNpYmlsaXTDqSBldCBsZSByYXlvbm5lbWVudCBzb2xhaXJlIHNvbnQgY29ycsOpbMOpcyBuw6lnYXRpdmVtZW50IMOgIGBkZXBfZGVsYXlgLiBRdWFuZCBsZXMgY29uZGl0aW9ucyBtw6l0w6lvcyBzZSBkw6lncmFkZW50IChtb2lucyBkZSBzb2xlaWwsIHRlbXDDqXJhdHVyZSBwbHVzIGZhaWJsZSwgcGx1cyBkZSBicm91aWxsYXJkIGRvbmMgbW9pbnMgZGUgdmlzaWJpbGl0w6kpIGxlcyByZXRhcmRzIGF1Z21lbnRlbnQgY2FyIGxlcyBhdmlvbnMgZXQgbGVzIHBpc3RlcyBkb2l2ZW50IMOqdHJlIChyZSltaXMgZW4gw6l0YXQgcGx1cyBzb3V2ZW50LiBDZWxhIGRlbWFuZGUgcGx1cyBkZSBtYWludGVuYW5jZS4KCiozLiBTZWxvbiB2b3VzLCBwb3VycXVvaSB0YW50IGRlIHBvaW50cyBzb250LWlsIHJlZ3JvdXDDqXMgcHLDqHMgZGUgKDAsIDApID8qCgpMYSBtYWpvcml0w6kgZGVzIHZvbHMgZMOpY29sbGVudCBldCBhdGVycmlzZW50IMOgIGwnaGV1cmUgb3UgYXZlYyBwZXUgZCdhdmFuY2Ugb3UgZGUgcmV0YXJkCgoqNC4gw4AgcXVvaSBsZSBwb2ludCAoMCwwKSBjb3JyZXNwb25kLWlsIHBvdXIgbGVzIHZvbHMgZOKAmUFsYXNrYSBBaXJsaW5lcyA/KgoKQ2VsYSBjb3JyZXNwb25kIMOgIGxhIHNpdHVhdGlvbiBpZMOpYWxlIGRlIGTDqWNvbGxhZ2UgZXQgZCdhdGVycmlzc2FnZSDDoCBsJ2hldXJlLgoKKjUuIENpdGV6IGxlcyDDqWzDqW1lbnRzIGRlIGNlIGdyYXBoaXF1ZS9kZSBjZXMgZG9ubsOpZXMgcXVpIHZvdXMgc2F1dGVudCBsZSBwbHVzIGF1eCB5ZXV4ID8qCgpJbCBzJ2FnaXQgaWNpIGRlIHLDqWFsaXNlciBsJ2V4ZXJjaWNlIHF1aSBlc3QgZGVtYW5kw6kgZGFucyBsYSBwYXJ0aWUgInLDqXN1bHRhdHMiIGRlIHRvdXQgcmFwcG9ydCBvdSBjb21wdGUgcmVuZHUuIEljaSwgaWwgeSBhIDQgY2hvc2VzIMOgIGTDqWNyaXJlIDoKCjEuIExhIHJlbGF0aW9uIGVudHJlIGxlcyAyIHZhcmlhYmxlcyBlc3QgcG9zaXRpdmUuCjIuIExhIHJlbGF0aW9uIGVudHJlIGxlcyAyIHZhcmlhYmxlcyBzZW1ibGUgbGluw6lhaXJlLgozLiBMYSBncmFuZGUgbWFqb3JpdMOpIGRlcyB2b2xzIGVzdCDDoCBwZXUgcHLDqHMgw6AgbCdoZXVyZSwgZGFucyB1bmUgZm91cmNoZXR0ZSBkZSAyNSBtaW51dGVzIGQnYXZhbmNlIG91IGRlIHJldGFyZCwgYXUgZMOpcGFydCBjb21tZSDDoCBsJ2Fycml2w6llLgo0LiBJbCB5IGEgdG91dGVmb2lzIHVuZSBmb3J0ZSB2YXJpYWJpbGl0w6kgYXZlYyBkZXMgdmFsZXVycyBleHRyw6ptZXMgdHLDqHMgZm9ydGVzIDogY2VydGFpbnMgdm9scyBhY2N1bXVsZW50IHBsdXMgZGUgM2ggZGUgcmV0YXJkLgoKT24gZGlzY3V0ZSBkb25jIGRlIGxhIG5hdHVyZSBkZSBsYSByZWxhdGlvbiwgZGUgc2EgZm9ybWUsIGRlIGxhIHRlbmRhbmNlIGNlbnRyYWxlIChvw7kgc29udCBzaXR1w6lzIGxhIG1ham9yaXTDqSBkZXMgcG9pbnRzKSBldCBkZXMgZXh0csOqbWVzLgoKKjYuIENyw6lleiB1biBub3V2ZWF1IG51YWdlIGRlIHBvaW50cyBlbiB1dGlsaXNhbnQgZOKAmWF1dHJlcyB2YXJpYWJsZXMgZHUgamV1IGRlIGRvbm7DqWVzIGBhbGFza2FfZmxpZ2h0c2AqCgpQb3VyIHRlbnRlciBkZSB2w6lyaWZpZXIgbm90cmUgaHlwb3Row6hzZSBzdXIgbGUgbGllbiBlbnRyZSBtw6l0w6lvIGV0IHJldGFyZCA6CmBgYHtyLCB0aWR5PUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpnZ3Bsb3QoZGF0YSA9IGFsYXNrYV9mbGlnaHRzLCBhZXMoeCA9IG1vbnRoLCB5ID0gYXJyX2RlbGF5KSkgKwogIGdlb21fcG9pbnQoKQpgYGAKCgojIyBFeGVyY2ljZXMgNC4zLjYKCkwnYXJndW1lbnQgYHN0cm9rZWAgcGVybWV0IGRlIHNww6ljaWZpZXIgbCfDqXBhaXNzZXVyIGRlcyB0cmFpdHMgdXRpbGlzw6lzIHBvdXIgdHJhY2VyIGxlcyBzeW1ib2xlcyAoY2VyY2xlcywgY2FycsOpcywgZXRjLikgc3VyIHVuIGdyYXBoaXF1ZS4KCkNyw6lhdGlvbiBkdSBqZXUgZGUgZG9ubsOpZXMgYGRpYW1zYCA6CmBgYHtyLCB0aWR5PUZBTFNFfQpzZXQuc2VlZCg0NTMyKSAjIEFmaW4gcXVlIHRvdXQgbGUgbW9uZGUgcsOpY3Vww6hyZSBsZXMgbcOqbWVzIGxpZ25lcwpkaWFtcyA8LSBkaWFtb25kcyAlPiUKICBzYW1wbGVfbig1MDAwKQpgYGAKCkNyw6lhdGlvbiBkdSBncmFwaGlxdWUgOgpgYGB7ciwgdGlkeT1GQUxTRSwgd2FybmluZz1GQUxTRX0KZ2dwbG90KGRpYW1zLCBhZXMoeCA9IGNhcmF0LCB5ID0gcHJpY2UsIGNvbG9yID0gY2xhcml0eSkpICsKICBnZW9tX3BvaW50KHNoYXBlID0gNCwgYWxwaGEgPSAwLjYpCmBgYAoKUmV2ZW5pciBzdXIgbGEgcG9zaXRpb24gZGVzIGFyZ3VtZW50cyBlc3Row6l0aXF1ZXMgOiBkYW5zIGBnZ3Bsb3QoKWAgb3UgZGFucyBgZ2VvbV9YWFgoKWAgPwoKKiDDgCBxdW9pIHNvbnQgZHVlcyBsZXMgYmFuZGVzIHZlcnRpY2FsZXMgPyoKCkxlcyBiYW5kZXMgdmVydGljYWxlcyBzb250IGxlIHLDqXN1bHRhdCBkJ2FwcHJveGltYXRpb25zIGh1bWFpbmVzLiBPbiBhIHRlbmRhbmNlIMOgIGFycm9uZGlyIGF1IGNhcmF0ICJyb25kIiBpbW3DqWRpYXRlbWVudCBzdXDDqXJpZXVyICgyIGF1IGxpZXUgZGUgMS45OCwgMS43IGF1IGxpZXUgZGUgMS42OSwgZXRjKSwgY2FyIGxhIHZhbGV1ciBtYXJjaGFuZGUgZMOpcGVuZCBkdSBjYXJhdC4KCiMjIEV4ZXJjaWNlcyA0LjQuMgoKQ3LDqWF0aW9uIGR1IGpldSBkZSBkb25uw6llcyBgc21hbGxfd2VhdGhlcmAgOgpgYGB7ciwgdGlkeT1GQUxTRX0Kc21hbGxfd2VhdGhlciA8LSB3ZWF0aGVyICU+JQogIGZpbHRlcihvcmlnaW4gPT0gIkVXUiIsCiAgICAgICAgIG1vbnRoID09IDEsCiAgICAgICAgIGRheSA8PSAxNSkKCnNtYWxsX3dlYXRoZXIKYGBgCgoqRXhwbGlxdWV6IHBvdXJxdW9pIGxhIHZhcmlhYmxlIGB0aW1lX2hvdXJgIGlkZW50aWZpZSBkZSBtYW5pw6hyZSB1bmlxdWUgbGUgbW9tZW50IG91IGNoYXF1ZSBtZXN1cmUgYSDDqXTDqSByw6lhbGlzw6llIGFsb3JzIHF1ZSBjZSBu4oCZZXN0IHBhcyBsZSBjYXMgZGUgbGEgdmFyaWFibGUgYGhvdXJgLioKCmBob3VyYCBlc3QgdW5lIHZhcmlhYmxlIGVudGnDqHJlIHF1aSBwcmVuZCBsZXMgdmFsZXVycyAwIMOgIDIzIGNoYXF1ZSBqb3VyIGRlIGwnYW5uw6llLiBJbmRpcXVlciBxdSd1bmUgbWVzdXJlIGEgw6l0w6kgZmFpdGUgw6AgMTNoIG4nZXN0IHBhcyBzdWZmaXNhbnQgY2FyIGVsbGUgYSBwdSDDqnRyZSBmYWl0ZSBuJ2ltcG9ydGUgcXVlIGpvdXIuIExhIHZhcmlhYmxlIGB0aW1lLWhvdXJgIGNvbnRpZW50IGxhIGRhdGUgZXQgbCdoZXVyZSBkZSBjaGFxdWUgbWVzdXJlLCBjZSBxdWkgZW4gZmFpdCB1biBpZGVudGlmaWFudCB1bmlxdWUgZGUgY2hhcXVlIG1lc3VyZS4KCgojIyBFeGVyY2ljZXMgNC42LjMKCipFeGFtaW5leiBsYSBmaWd1cmUgNC4zNy4qCgoqMS4gUXVlbHMgw6lsw6ltZW50cyBub3V2ZWF1eCBjZSBncmFwaGlxdWVzIG5vdXMgYXBwcmVuZC1pbCBwYXIgcmFwcG9ydCBhdSBncmFwaGlxdWUgNC4zNCBjaS1kZXNzdXMgPyBDb21tZW50IGxlIOKAnGZhY2V0aW5n4oCdIG5vdXMgYWlkZS10J2lsIMOgIHZpc3VhbGlzZXIgbGVzIHJlbGF0aW9ucyBlbnRyZSAyIChvdSAzKSB2YXJpYWJsZXMgPyoKCkxhIG5hdHVyZSBiaW1vZGFsZSBkZSBsYSBkaXN0cmlidXRpb24gZGUgbGEgcHJlbWnDqHJlIGZpZ3VyZSBjYWNoZSBlbiBmYWl0IHVuZSBkaXN0cmlidXRpb24gbWVuc3VlbGxlIGRlcyB0ZW1ww6lyYXR1cmVzIHVuaW1vZGFsZSB0b3V0IMOgIGZhaXQgY2xhc3NpcXVlLCBldCBjZSwgcG91ciBsZXMgMyBhw6lyb3BvcnRzLiBDZSBncmFwaGlxdWUgbm91cyBwZXJtZXQgw6lnYWxlbWVudCBkZSB2aXN1YWxpc2VyIGxhIHZhcmlhYmlsaXTDqSBtZW5zdWVsbGUsIGFubnVlbGxlLCBldCBlbnRyZSBsZXMgYcOpcm9wb3J0cyBjZSBxdWkgw6l0YWl0IGltcG9zc2libGUgYXZlYyBsJ2hpc3RvZ3JhbW1lIHN5bnRow6l0aXF1ZSBkZSBsYSBmaWd1cmUgNC4zNC4KCiogTGVzIGNvbXBhcmFpc29ucyBlbiBsaWduZSBub3VzIHBlcm1ldHRlbnQgZGUgY29tcGFyZXIgbGVzIHRlbXDDqXJhdHVyZSBwb3VyIHVuIG1vaXMgZG9ubsOpIGVudHJlIGxlcyAzIGHDqXJvcG9ydHMuIE5vdXMgY29uc3RhdG9ucyBpY2kgcXVlIGNlcyB0ZW1ww6lyYXR1cmVzIHNvbnQgdG91am91cnMgdHLDqHMgc2ltaWxhaXJlcy4gTGVzIGRpc3RyaWJ1dGlvbnMgZGVzIHRlbXDDqXJhdHVyZXMgZGFucyBsZXMgMyBhw6lyb3BvcnRzIGRlIE5ZIHNvbnQgdHLDqHMgcHJvY2hlcy4gQ2UgcXVpIGVzdCBmaW5hbGVtZW50IHRvdXQgw6AgZmFpdCBub3JtYWwgY29tcHRlIHRlbnUgZGUgbGV1ciBwcm94aW1pdMOpIGfDqW9ncmFwaGlxdWUuCiogTGVzIGNvbXBhcmFpc29ucyBlbiBjb2xvbm5lcyBub3VzIHBlcm1ldHRlbnQgZGUgdmlzdWFsaXNlciBsJ8Opdm9sdXRpb24gbWVuc3VlbGxlIGRlcyB0ZW1ww6lyYXR1cmVzIHBvdXIgdW4gYcOpcm9wb3J0IGRvbm7DqS4gSWNpLCBvbiBjb25zdGF0ZSBxdWUgbCdhbXBsaXR1ZGUgdmFyaWUsIGRlIG3Dqm1lIHF1ZSBsYSBwb3NpdGlvbiBkZXMgcGljcyA6IGlsIGZhaXQgcGx1cyBjaGF1ZCBsJ8OpdMOpIHF1ZSBsJ2hpdmVyLiBMw6AgZW5jb3JlLCBjJ2VzdCBsb2dpcXVlLCBub3VzIHNvbW1lcyBkYW5zIGwnaMOpbWlzcGjDqHJlIE5vcmQuCgoKKjIuIMOAIHF1b2kgY29ycmVzcG9uZGVudCBsZXMgbnVtw6lyb3MgMSDDoCAxMiA/KiAKCkF1eCBtb2lzIGRlIGwnYW5uw6llCgoqMy4gw4AgcXVvaSBjb3JyZXNwb25kZW50IGxlcyBjaGlmZnJlcyAyNSwgNTAsIDc1LCAxMDAgPyoKCkF1eCB0ZW1ww6lyYXR1cmVzIGVuIGRlZ3LDqXMgZmFyZW5oZWl0CgoqNC4gw4AgcXVvaSBjb3JyZXNwb25kZW50IGxlcyBjaGlmZnJlcyAwLCAxMDAsIDIwMCwgMzAwID8qCgpBdSBub21icmUgZCdvYnNlcnZhdGlvbnMgcG91ciBjaGFxdWUgY2F0w6lnb3JpZQoKKjUuIE9ic2VydmV6IGxlcyDDqWNoZWxsZXMgZGVzIGF4ZXMgYHhgIGV0IGB5YCBwb3VyIGNoYXF1ZSBzb3VzIGdyYXBoaXF1ZS4gUXUnb24tdCdlbGxlcyBkZSBwYXJ0aWN1bGllciA/IEVuIHF1b2kgZXN0LWNlIHV0aWxlID8qCgpFbGxlcyBzb250IHRvdXRlcyBpZGVudGlxdWVzLiBFbGxlcyBmYWNpbGl0ZW50IGFpbnNpIGxlcyBjb21wYXJhaXNvbnMgZW50cmUgc291cy1ncmFwaGlxdWVzLiBOb3VzIHZlcnJvbnMgcGV1dC3DqnRyZSBwbHVzIHRhcmQgcXVlIGNlIG4nZXN0IHBhcyB0b3Vqb3VycyBzb3VoYWl0YWJsZS4KCio2LiBMYSB2YXJpYWJpbGl0w6kgZGVzIHRlbXDDqXJhdHVyZXMgZXN0LWVsbGUgcGx1cyBpbXBvcnRhbnRlIGVudHJlIGxlcyBhw6lyb3BvcnRzLCBlbnRyZSBsZXMgbW9pcywgb3UgYXUgc2VpbiBkZXMgbW9pcyA/IEV4cGxpcXVleiB2b3RyZSByw6lmbGV4aW9uLioKCkxhIHZhcmlhYmlsaXTDqSBkZXMgdGVtcMOpcmF0dXJlcyBlbnRyZSBhw6lyb3BvcnRzIGVzdCB0csOocyBmYWlibGUgOiBwb3VyIHVuIG1vaXMgZG9ubsOpLCBsZXMgdGVtcMOpcmF0dXJlcyBvYnNlcnbDqWVzIHNvbnQgZGlzdHJpYnXDqWVzIGRlIGZhw6dvbiB0csOocyBzaW1pbGFpcmUgZGFucyBsZXMgMyBhw6lyb3BvcnRzLiBMYSB2YXJpYWJpbGl0w6kgZW50cmUgbGVzIG1vaXMgZXN0IHBsdXMgaW1wb3J0YW50ZSA6IGF1IHNlaW4gZCd1biBtb2lzLCBsYSBkaXN0cmlidXRpb24gZGVzIHRlbXDDqXJhdHVyZXMgY291dnJlIHJhcmVtZW50IHBsdXMgZGUgMzDCuiBGYXJlbmhlaXQgYWxvcnMgcXUnZWxsZSBhdHRlaW50IHByZXNxdWUgNTDCuiBGYXJlbmhlaXQgZW50cmUgbGVzIG1vaXMgZCfDqXTDqSBldCBsZXMgbW9pcyBkJ2hpdmVyLgoKCiMjIEV4ZXJjaWNlIDQuOC4zCgoqMS4gUXVlbGxlIGVzdCBsYSBkaWZmw6lyZW5jZSBlbnRyZSB1biBoaXN0b2dyYW1tZSBldCB1biBkaWFncmFtbWUgYsOidG9ucyA/KgoKKiBVbiBoaXN0b2dyYW1tZSBwZXJtZXQgZGUgdmlzdWFsaXNlciBsYSBkaXN0cmlidXRpb24gZCd1bmUgdmFyaWFibGUgY29udGludWUuCiogVW4gZGlhZ3JhbW1lIGLDonRvbnMgcGVybWV0IGRlIHZpc3VhbGlzZXIgbGEgZGlzdHJpYnV0aW9uIGQndW5lIHZhcmlhYmxlIGNhdMOpZ29yaWVsbGUKCioyLiBQb3VycXVvaSBsZXMgaGlzdG9ncmFtbWVzIHNvbnQtaWxzIGluYWRhcHTDqXMgcG91ciB2aXN1YWxpc2VyIGRlcyBkb25uw6llcyBjYXTDqWdvcmllbGxlcyA/KgoKUGFyY2UgcXVlIGxlcyBiYXJyZXMgc29udCBjb2xsw6llcyBsZXMgdW5lcyBhdXggYXV0cmVzIGNlIHF1aSBkb25uZSB1bmUgaW1wcmVzc2lvbiB2aXN1ZWxsZSBkZSBjb250aW51aXTDqS4gUGFyIGFpbGxldXJzLCBsZXMgYmFycmVzIG5lIHBldXZlbnQgcGFzIMOqdHJlIHLDqS1vcmRvbm7DqWVzLCBjZSBxdWkgZXN0IHNvdXZlbnQgbsOpY2Vzc2FpcmUgcG91ciB1bmUgdmFyaWFibGUgY2F0w6lnb3JpZWxsZS4KCiozLiBRdWVsIGVzdCBsZSBub20gZGUgbGEgY29tcGFuaWUgcG91ciBsYXF1ZWxsZSBsZSBwbHVzIGdyYW5kIG5vbWJyZSBkZSB2b2xzIG9udCBxdWl0dMOpIE5ldyBZb3JrIGVuIDIwMTMgKGplIHZldXggY29ubmHDrnRyZSBzb24gbm9tLCBwYXMganVzdGUgc29uIGNvZGUpID8gT8O5IHNlIHRyb3V2ZSBjZXR0ZSBpbmZvcm1hdGlvbiA/KgoKT24gZXhhbWluZSBsYSBmaWd1cmUgNC40NyAocGFyIGV4ZW1wbGUpIGV0IG9uIGNvbnN0YXRlIHF1ZSBsYSBjb21wYWduaWUgZW4gcXVlc3Rpb24gYSBsZSBjb2RlIGBVQWAuIExlIG5vbSBjb21wbGV0IGRlIGNldHRlIGNvbXBhZ25pZSBzZSB0cm91dmUgZGFucyBsZSB0YWJsZWF1IGBhaXJsaW5lc2AuIENlIHRhYmxlYXUgZmFpdCBsZSBsaWVuIGVudHJlIGNvZGVzIGV0IG5vbSBkZXMgY29tcGFnbmllcyBhw6lyaWVubmVzLgoKTGEgY29tcGFnbmllIHF1aSBhIGFmZnLDqXTDqSBsZSBwbHVzIGdyYW5kIG5vbWJyZSBkZSB2b2xzIGF1IGTDqXBhcnQgZGUgTmV3IFlvcmsgZW4gMjAxMyBlc3QgVW5pdGVkIEFpciBMaW5lcyBJbmMuCgpgYGB7cn0KYWlybGluZXMKYGBgCgoqNC4gUXVlbCBlc3QgbGUgbm9tIGRlIGxhIGNvbXBhbmllIHBvdXIgbGFxdWVsbGUgbGUgcGx1cyBwZXRpdCBub21icmUgZGUgdm9scyBvbnQgcXVpdHTDqSBOZXcgWW9yayBlbiAyMDEzIChqZSB2ZXV4IGNvbm5hw650cmUgc29uIG5vbSwgcGFzIGp1c3RlIHNvbiBjb2RlKSA/IE/DuSBzZSB0cm91dmUgY2V0dGUgaW5mb3JtYXRpb24gPyoKCk3Dqm1lIGTDqW1hcmNoZSA6IGlsIHMnYWdpdCBkZSBsYSBjb21wYWduaWUgU2t5d2VzdCBBaXJsaW5lcyBJbmMuIChjb2RlIGBPT2ApLgoKIyMgRXhlcmNpY2VzIDQuOC40CgpMYSBmaWd1cmUgNC40NyBldCBsYSB0YWJsZWF1IGBjYXJyaWVyX3RhYmxlYCBwZXJtZXR0ZW50IGRlIHLDqXBvbmRyZSBhdXggcXVlc3Rpb25zIHN1aXZhbnRlcwoKKkNvbXBhcmV6IGxlcyBjb21wYWduaWVzIEV4cHJlc3NKZXQgQWlybGluZXMgKEVWKSBldCBVUyBBaXJ3YXlzIChVUykuIERlIGNvbWJpZW4gZGUgZm9pcyBsYSBwYXJ0IGRlIEVWIGVzdC1lbGxlIHN1cMOpcmlldXJlIMOgIGNlbGxlIGTigJlVUyA/ICgyIGZvaXMsIDMgZm9pcywgMS4yIGZvaXMgP+KApikqCgpVbiBwZXUgcGx1cyBkZSAyLDUKCipRdWVsbGUgZXN0IGxhIHRyb2lzacOobWUgY29tcGFnbmllIGHDqXJpZW5uZSBsYSBwbHVzIGltcG9ydGFudGUgZW4gdGVybWUgZGUgbm9tYnJlIGRlIHZvbHMgYXUgZMOpcGFydCBkZSBOZXcgWW9yayBlbiAyMDEzID8qCgpFeHByZXNzSmV0IEFpcmxpbmVzIChgRVZgKQoKKkNvbWJpZW4gZGUgY29tcGFuaWVzIGHDqXJpZW5uZXMgb250IG1vaW5zIGRlIHZvbHMgcXVlIFVuaXRlZCBBaXJsaW5lcyAoYFVBYCkgPyoKClRvdXRlcywgc29pdCAxNSBhdSB0b3RhbAoKIyMgRXhlcmNpY2VzIDQuMTAKCgpDcsOpYXRpb24gZHUgamV1IGRlIGRvbm7DqWVzIDoKYGBge3IsIHRpZHkgPSBGQUxTRX0KIyBPbiBmaXhlIGxlIGfDqW7DqXJhdGV1ciBkZSBub21icmVzIGFsw6lhdG9pcmVzIHBvdXIgY2hvaXNpciBsZXMgbcOqbWVzIGxpZ25lcyBxdWUgZGFucyBsJ2V4ZW1wbGUgZHUgbGl2cmUKc2V0LnNlZWQoMTIzNCkKCiMgQ3LDqWF0aW9uIGRlIHNtYWxsX2ZsaWdodHMKc21hbGxfZmxpZ2h0cyA8LSBmbGlnaHRzICU+JQogIHNhbXBsZV9uKDEwMDApICU+JQogIGZpbHRlcighaXMubmEoYXJyX2RlbGF5KSwKICAgICAgICAgZGlzdGFuY2UgPCAzMDAwKQoKc21hbGxfZmxpZ2h0cwpgYGAKCkNyw6lhdGlvbiBkdSBwcmVtaWVyIGdyYXBoaXF1ZQpgYGB7ciwgdGlkeSA9IEZBTFNFfQpnZ3Bsb3Qoc21hbGxfZmxpZ2h0cywgYWVzKHggPSBkaXN0YW5jZSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IGFpcl90aW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gb3JpZ2luLAogICAgICAgICAgICAgICAgICAgICAgICAgIHNoYXBlID0gb3JpZ2luKSkgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjgpICsKICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJTZXQxIikgKwogIGxhYnMoeCA9ICJEaXN0YW5jZSBwYXJjb3VydWUgZW4gdm9sIChtaWxlcykiLAogICAgICAgeSA9ICJUZW1wcyBkZSB2b2wgKG1pbnV0ZXMpIiwKICAgICAgIHRpdGxlID0gIlJlbGF0aW9uIGVudHJlIGxlIHRlbXBzIGRlIHZvbCBldCBsYSBkaXN0YW5jZSBwYXJjb3VydWUiLAogICAgICAgc3VidGl0bGUgPSAiU2V1bHMgbGVzIHZvbHMgYXUgZMOpcGFydCBkZSBKRksgZXQgTmV3YXJrIHBhcmNvdXJlbnQgcGx1cyBkZSAxNjAwIG1pbGVzIiwKICAgICAgIGNvbG9yID0gIkHDqXJvcG9ydCBkZVxuTmV3IFlvcmsiLAogICAgICAgc2hhcGUgPSAiQcOpcm9wb3J0IGRlXG5OZXcgWW9yayIsCiAgICAgICBjYXB0aW9uID0gIkRvbm7DqWVzIDogc21hbGxfZmxpZ2h0cyIpICsKICB0aGVtZV9idygpCmBgYAoKCkNyw6lhdGlvbiBkdSBzZWNvbmQgZ3JhcGhpcXVlCmBgYHtyLCB0aWR5ID0gRkFMU0V9CmdncGxvdChzbWFsbF9mbGlnaHRzLCBhZXMoeCA9IGZhY3Rvcihtb250aCksIGZpbGwgPSBvcmlnaW4pKSArCiAgZ2VvbV9iYXIoKSArCiAgZmFjZXRfd3JhcCh+IGZjdF9pbmZyZXEoY2FycmllciksIG5yb3cgPSA0KSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJBY2NlbnQiKSArCiAgbGFicyh4ID0gIk1vaXMgZGUgbCdhbm7DqWUgMjAxMyIsCiAgICAgICB5ID0gIk5vbWJyZSBkZSB2b2xzIiwKICAgICAgIHRpdGxlID0gIsOJdm9sdXRpb24gbWVuc3VlbGxlIGR1IHRyYWZpYyBhw6lyaWVuIE5ldyBZb3JrYWlzIGVuIDIwMTMiLAogICAgICAgc3VidGl0bGUgPSAiU2V1bGVzIDkgY29tcGFnbmllcyBhw6lyaWVubmVzIHN1ciAxNCBvbnQgZMOpc2VydmkgTmV3IFlvcmsgdG91dGUgbCdhbm7DqWUiLAogICAgICAgZmlsbCA9ICJBw6lyb3BvcnQgZGVcbk5ldyBZb3JrIiwKICAgICAgIGNhcHRpb24gPSAiRG9ubsOpZXMgOiBzbWFsbF9mbGlnaHRzIikgKwogIHRoZW1lX2J3KCkKYGBgCgpgYGB7cn0Kc21hbGxfZmxpZ2h0cwpgYGAKCgojIENoYXBpdHJlIDUKCiMjIEV4ZXJjaWNlIDUuMi40CgoqRXhhbWluZXogbGVzIHRhYmxlYXV4IHJhdGVzLCBzdG9ybXMgZXQgcG9wdWxhdGlvbiBkdSBwYWNrYWdlIEVEQVdSLioKCi0gQ2VzIHRhYmxlYXV4IHNvbnQtaWxzIGRlcyDigJx0YWJsZWF1eCByYW5nw6lz4oCdICh0aWR5IGRhdGEpID8KLSBTaSBvdWksIHF1ZWxsZXMgc29udCBsZXMgdmFyaWFibGVzIHJlcHLDqXNlbnTDqWVzID8KLSBTaSBub24sIHRyYW5zZm9ybWV6LWxlcyBlbiDigJx0YWJsZWF1eCByYW5nw6lz4oCdLgoKYGBge3IsIHRpZHk9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiMgSW5zdGFsbGF0aW9uIGV0IG1pc2UgZW4gbcOpbW9pcmUgZGVzIHBhY2thZ2VzIG7DqWNlc3NhaXJlcwpsaWJyYXJ5KHRpZHl2ZXJzZSkKCiMgaW5zdGFsbC5wYWNrYWdlcygiZGV2dG9vbHMiKQojIGxpYnJhcnkoZGV2dG9vbHMpCgojIGluc3RhbGxfZ2l0aHViKCJyc3R1ZGlvL0VEQVdSIikKbGlicmFyeShFREFXUikKYGBgCgpBZmZpY2hhZ2UgZGVzIHRhYmxlYXV4IDoKYGBge3J9CnJhdGVzCmBgYAoKYHJhdGVzYCBlc3QgdW4gdGFibGVhdSByYW5nw6kgYXZlYyB1bmUgdmFyaWFibGUgcGFyIGNvbG9ubmUgKHBheXMsIGFubsOpZSwgbm9tYnJlIGRlIGNhcyBkZSB0dWJlcmN1bG9zZSwgcG9wdWxhdGlvbiBnbG9iYWxlLCBldCB0YXV4IGRlIG1hbGFkZXMpIGV0IHVuZSBsaWduZSBwYXIgb2JzZXJ2YXRpb24uCgpgYGB7cn0Kc3Rvcm1zCmBgYAoKYHN0b3Jtc2AgZXN0IHVuIHRhYmxlYXUgcmFuZ8OpIGF2ZWMgdW5lIHZhcmlhYmxlIHBhciBjb2xvbm5lIChub20gZGUgbGEgdGVtcMOqdGUsIHZpdGVzc2UgZHUgdmVudCBlbiBtcGgsIHByZXNzaW9uIGVuIG1pbGxpYmFycyBldCBkYXRlIMOgIGxhcXVlbGxlIGxhIHBsdXMgZm9ydGUgdml0ZXNzZSBkZSB2ZW50IGEgw6l0w6kgZW5yZWdpc3Ryw6llKSBldCB1bmUgbGlnbmUgcGFyIG9ic2VydmF0aW9uLgoKYGBge3J9CnBvcHVsYXRpb24KYGBgCgpgcG9wdWxhdGlvbmAgbidlc3QgcGFzIHVuIHRhYmxlYXUgcmFuZ8OpLiBMZXMgdmFyaWFibGVzIGRldnJhaWVudCDDqnRyZSBwYXlzLCBhbm7DqWUgZXQgcG9wdWxhdGlvbgoKYGBge3J9CnBpdm90X2xvbmdlcihwb3B1bGF0aW9uLCBjb2wgPSBgMTk5NWA6YDIwMTNgLCBuYW1lc190byA9ICJ5ZWFyIiwgdmFsdWVzX3RvID0gInBvcCIpCmBgYAoKCiMjIEV4ZXJjaWNlIDUuMy40CgoqMS4gTOKAmW9iamV0IGRhdXBoaW4gZXN0LWlsIOKAnHRpZHnigJ0gKGF1dHJlbWVudCBkaXQsIHPigJlhZ2l0LWlsIGRlIOKAnGRvbm7DqWVzIHJhbmfDqWVz4oCdKSA/IEp1c3RpZmllei4qCgpgYGB7cn0KIyBJbXBvcnRhdGlvbiBkdSBqZXUgZGUgZG9ubsOpZXMgYGRhdXBoaW5gIDoKbGlicmFyeShyZWFkeGwpCmRhdXBoaW4gPC0gcmVhZF9leGNlbCgiZGF1cGhpbi54bHMiLCBuYSA9ICIqIiwgc2tpcCA9IDkpCm5hbWVzKGRhdXBoaW4pIDwtIGMoIklEIiwgIlNleGUiLCAiU3RhdHV0IiwgIlRhaWxsZSIsICJBZ2UiLCAiQ2QiLCAiQ3UiLCAiSGciLCAiT3JnYW5lIikKZGF1cGhpbgpgYGAKCk91aSwgYGRhdXBoaW5gIGVzdCAidGlkeSIuIElsIGNvbnRpZW50IHVuZSB2YXJpYWJsZSBwYXIgY29sb25uZSwgdW5lIG9ic2VydmF0aW9uIHBhciBsaWduZS4gTGEgdmFyaWFibGUgSUQgcG91cnJhaXQgdG91dGVmb2lzIMOqdHJlIHN1cHByaW3DqWUuCgoqMi4gUHJvZHVpc2V6IGxlIGdyYXBoaXF1ZSBjaS1kZXNzb3VzKgoKYGBge3IsIHRpZHk9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy5oZWlnaHQ9NCwgZmlnLndpZHRoPTR9CmdncGxvdChkYXVwaGluLCBhZXMoeCA9IEFnZSwgeSA9IEhnLCBjb2xvciA9IFNleGUpKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIikgKwogIGdlb21fcG9pbnQoKSArCiAgZmFjZXRfd3JhcCh+IE9yZ2FuZSwgbnJvdyA9IDIsIHNjYWxlcyA9ICJmcmVlX3kiKSArCiAgbGFicyh4ID0gIsOCZ2UgKGFubsOpZXMpIiwKICAgICAgIHkgPSAiQ29uY2VudHJhdGlvbiBlbiBNZXJjdXJlIChtZy9rZykiLAogICAgICAgdGl0bGUgPSAiw4l2b2x1dGlvbiBkZSBsYSBjb25jZW50cmF0aW9uIGVuIG1lcmN1cmUgYWdlIGwnw6JnZSBjaGV6IERlbHBoaW51cyBkZWxwaGlzIiwKICAgICAgIGNvbG9yID0gIlNleGUiLAogICAgICAgY2FwdGlvbiA9ICJEb25uw6llcyA6IGRhdXBoaW4ueGxzIikgKwogIHRoZW1lX2J3KCkKYGBgCgoKKjMuIEltcG9ydGV6IGRhbnMgUiBsZSBqZXUgZGUgZG9ubsOpZXMgd2hvVEIuY3N2LiBDZSBqZXUgZGUgZG9ubsOpZXMgY29udGllbnQgbGVzIGNhcyBkZSB0dWJlcmN1bG9zZSAoVEIpIHJhcHBvcnTDqXMgcGFyIGzigJlPcmdhbmlzYXRpb24gTW9uZGlhbGUgZGUgbGEgU2FudMOpIChPTVMsIG91IFdITyBlbiBhbmdsYWlzIDogV29ybGQgSGVhbHRoIE9yZ2FuaXphdGlvbikuIExlcyBjYXMgc29udCByw6lwZXJ0b3Jpw6lzIHBhciBhbm7DqWUsIHBheXMsIMOiZ2UsIHNleGUsIHR5cGUgZGUgdHViZXJjdWxvc2UgZXQgbcOpdGhvZGUgZGUgZGlhZ25vc3RpcXVlLiBTZWxvbiB2b3VzLCBjZSBqZXUgZGUgZG9ubsOpZXMgZXN0LWlsIOKAnHJhbmfDqeKAnSA/IFBvdXJxdW9pID8qCgpgYGB7cn0KbGlicmFyeShyZWFkcikKd2hvVEIgPC0gcmVhZF9jc3YoIndob1RCLmNzdiIpCndob1RCCmBgYAoKQ2UgamV1IGRlIGRvbm7DqWVzIG4nZXN0IHBhcyByYW5nw6kgY2FyIG91dHJlIGxlcyA0IHByZW1pw6hyZXMgY29sb25uZXMsIHRvdXRlcyBsZXMgYXV0cmVzIGNvbnRpZW5uZW50IGRlIG11bHRpcGxlcyBpbmZvcm1hdGlvbnMgOiBub3V2ZWF1eCBjYXMgb3UgYW5jaWVucyAoaWNpLCBpbHMgc29udCB0b3VzIG5vdXZlYXV4KSwgdHlwZSBkZSBjYXMgKGBzcGAsIGBzbmAsIGBlcGAgb3UgYHJlbGApLCBzZXhlIGRlcyBwYXRpZW50cyAoYG1gIG91IGBmYCkgZXQgZW5maW4gdHJhbmNoZXMgZCfDomdlcy4gTm91cyBkZXZyaW9ucyBkb25jIGF2b2lyIGNlcyA0IHZhcmlhYmxlcyBzdXBwbMOpbWVudGFpcmVzLCBhdmVjIHBvdXIgY2hhcXVlIGNvbWJpbmFpc29uIHBvc3NpYmxlLCBsZSBub21icmUgZGUgY2FzIGRlIHR1YmVyY3Vsb3NlIHJhcHBvcnTDqWUgZGFucyBsZXMgY29sb25uZXMuCgoKKjQuIFNpIGNlIGpldSBkZSBkb25uw6llcyBu4oCZZXN0IHBhcyByYW5nw6ksIHJhbmdlei1sZSBlbiB1dGlsaXNhbnQgbGVzIGZvbmN0aW9ucyBkdSBwYWNrYWdlcyB0aWR5ciBxdWUgbm91cyBhdm9ucyBkw6ljb3V2ZXJ0ZXMgZGFucyBjZSBjaGFwaXRyZSA6IHBpdm90X2xvbmdlcigpLCBwaXZvdF93aWRlcigpLCBzZXBhcmF0ZSgpIGV0IHVuaXRlKCkgKHZvdXMgbuKAmWF1cmV6IHBhcyBuw6ljZXNzYWlyZW1lbnQgYmVzb2luIGTigJl1dGlsaXNlciBjZXMgNCBmb25jdGlvbnMsIGV0IMOgIGzigJlpbnZlcnNlLCBjZXJ0YWluZXMgZGV2cm9udCBwZXV0LcOqdHJlIMOqdHJlIHV0aWxpc8OpZXMgcGx1c2lldXJzIGZvaXMpLioKCk9uIGNvbW1lbmNlIGRvbmMgcGFyIHJhc3NlbWJsZXIgdG91dGVzIGxlcyBjb2xvbm5lcyBlbiB1bmUgc2V1bGUsIG5vbW3DqWUgYGNvbXBvc2l0ZWAgZXQgdG91cyBsZXMgY2hpZmZyZXMgZGFucyB1bmUgY29sb25uZSBgY2FzZXNgIDoKYGBge3J9Cndob1RCICU+JSAKICBwaXZvdF9sb25nZXIoY29scyA9IGBuZXdfc3BfbTAxNGA6YG5ld19yZWxfZjY1YCwgbmFtZXNfdG8gPSAiY29tcG9zaXRlIiwgdmFsdWVzX3RvID0gImNhc2VzIikKYGBgCgpQdWlzcXUnaWxzIHkgYSBiZWF1Y291cCBkZSBkb25uw6llcyBtYW5xdWFudGVzLCBvbiBsZXMgc3VwcHJpbWUuIENlbGEgbm91cyBwZXJtZXQgZGUgcGFzc2VyIGRlIHBsdXMgZGUgNDAwMDAwIGxpZ25lcyDDoCBlbnZpcm9uIDc2MDAwCgpgYGB7cn0Kd2hvVEIgJT4lIAogIHBpdm90X2xvbmdlcihjb2xzID0gYG5ld19zcF9tMDE0YDpgbmV3X3JlbF9mNjVgLCBuYW1lc190byA9ICJjb21wb3NpdGUiLCAKICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gImNhc2VzIiwgdmFsdWVzX2Ryb3BfbmEgPSBUUlVFKQpgYGAKCklsIG5vdXMgZmF1dCBlbnN1aXRlIHPDqXBhcmVyIGxhIGNvbG9ubmUgY29tcG9zaXRlIGVuIHBsdXNpZXVycyBjb2xvbm5lcy4gT24gdmEgc2Ugc2VydmlyIGRlcyBgX2AgcG91ciBpc29sZXIgYG5ld2AsIGxlIHR5cGUgZGUgY2FzLCBldCBsYSBjb21iaW5haXNvbiBgU2V4ZUFnZWAgOgoKYGBge3J9Cndob1RCICU+JSAKICBwaXZvdF9sb25nZXIoY29scyA9IGBuZXdfc3BfbTAxNGA6YG5ld19yZWxfZjY1YCwgbmFtZXNfdG8gPSAiY29tcG9zaXRlIiwgCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJjYXNlcyIsIHZhbHVlc19kcm9wX25hID0gVFJVRSkgJT4lIAogIHNlcGFyYXRlKGNvbCA9IGNvbXBvc2l0ZSwgaW50byA9IGMoIm5ldyIsICJ0eXBlIiwgIlNleGVBZ2UiKSkKYGBgCgpOb3VzIHBvdXZvbnMgZW5maW4gc8OpcGFyZXIgbGEgY29sb25uZSBgU2V4ZUFnZWAgZW4gMiBlbiBwbGHDp2FudCBsZSBwcmVtaWVyIGNhcmFjdMOocmUgZGFucyB1bmUgY29sb25uZSBgc2V4ZWAgZXQgbGUgcmVzdGUgZGFucyB1bmUgY29sb25uZSBgYWdlX2NsYXNzYCA6CgpgYGB7cn0Kd2hvVEIgJT4lIAogIHBpdm90X2xvbmdlcihjb2xzID0gYG5ld19zcF9tMDE0YDpgbmV3X3JlbF9mNjVgLCBuYW1lc190byA9ICJjb21wb3NpdGUiLCAKICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gImNhc2VzIiwgdmFsdWVzX2Ryb3BfbmEgPSBUUlVFKSAlPiUgCiAgc2VwYXJhdGUoY29sID0gY29tcG9zaXRlLCBpbnRvID0gYygibmV3IiwgInR5cGUiLCAiU2V4ZUFnZSIpKSAlPiUgCiAgc2VwYXJhdGUoY29sID0gU2V4ZUFnZSwgaW50byA9IGMoInNleGUiLCAiYWdlX2NsYXNzIiksIHNlcCA9IDEpCmBgYAoKUG91ciBmYWlyZSBib25uZSBtZXN1cmUsIG9uIHBldXQgc3VwcHJpbWVyIGxlcyBjb2xvbm5lcyBgaXNvMmAgZXQgYGlzbzNgIGNhciBlbGxlcyBzb250IHJlZG9uZGFudGVzIGF2ZWMgYGNvdW50cnlgLCBhaW5zaSBxdWUgYG5ld2AgcXVpIG5lIGNvbnRpZW50IGF1Y3VuZSBpbmZvcm1hdGlvbi4gT24gc3RvY2tlIGVuZmluIGxlIHLDqXN1bHRhdCBkYW5zIHVuIHRhYmxlYXUgbm9tbcOpIGB0aWR5VEJgIDoKCmBgYHtyfQp0aWR5VEIgPC0gd2hvVEIgJT4lIAogIHBpdm90X2xvbmdlcihjb2xzID0gYG5ld19zcF9tMDE0YDpgbmV3X3JlbF9mNjVgLCBuYW1lc190byA9ICJjb21wb3NpdGUiLCAKICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gImNhc2VzIiwgdmFsdWVzX2Ryb3BfbmEgPSBUUlVFKSAlPiUgCiAgc2VwYXJhdGUoY29sID0gY29tcG9zaXRlLCBpbnRvID0gYygibmV3IiwgInR5cGUiLCAiU2V4ZUFnZSIpKSAlPiUgCiAgc2VwYXJhdGUoY29sID0gU2V4ZUFnZSwgaW50byA9IGMoInNleGUiLCAiYWdlX2NsYXNzIiksIHNlcCA9IDEpICU+JSAKICBzZWxlY3QoLWlzbzIsIC1pc28zLCAtbmV3KQoKdGlkeVRCCmBgYAoKSWwgZXN0IG1haW50ZW5hbnQgZmFjaWxlIGRlIGZhaXJlIGRlcyBncmFwaGlxdWVzIGV4cGxvcmF0b2lyZS4gUGFyIGV4ZW1wbGUgOgoKYGBge3IsIHRpZHk9RkFMU0V9CnRpZHlUQiAlPiUgCiAgZmlsdGVyKGNvdW50cnkgPT0gIkZyYW5jZSIpICU+JSAKICBnZ3Bsb3QoYWVzKHggPSB5ZWFyLCB5ID0gY2FzZXMsIGNvbG9yID0gc2V4ZSkpICsKICAgIGdlb21fbGluZSgpICsKICAgIGZhY2V0X3dyYXAofmFnZV9jbGFzcywgbmNvbCA9IDIpICsKICAgIGxhYnModGl0bGUgPSAiTm91dmVhdXggY2FzIGRlIHR1YmVyY3Vsb3NlIGVuIEZyYW5jZSwgZGUgMTk5NSDDoCAyMDEzIiwKICAgICAgICAgY2FwdGlvbiA9ICJEb25uw6llcyA6IE9NUyIpCmBgYAoKCi0tLS0gCgojIENoYXBpdHJlIDYKCk1pc2UgZW4gbcOpbW9pcmUgZGVzIHBhY2thZ2VzIHV0aWxlcwoKYGBge3IsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShueWNmbGlnaHRzMTMpCmBgYAoKIyMgRXhlcmNpY2UgNi40LjIKCipEYW5zIGxhIHNlY3Rpb24gMy4zLjEsIG5vdXMgYXZvbnMgdXRpbGlzw6kgbGEgZm9uY3Rpb24gVmlldyBldCBs4oCZYXBwbGljYXRpb24gbWFudWVsbGUgZGUgZmlsdHJlcyBwb3VyIGTDqXRlcm1pbmVyIGNvbWJpZW4gZGUgdm9scyBhdmFpZW50IHF1aXR0w6kgbOKAmWHDqXJvcG9ydCBKRksgbGUgMTIgZsOpdnJpZXIgMjAxMy4gRW4gdXRpbGlzYW50IGxhIGZvbmN0aW9uIGZpbHRlcigpLCBjcsOpZXogdW4gb2JqZXQgbm9tbcOpIGBKRktfMTJmZXZgIHF1aSBjb250aWVuZHJhIGxlcyBkb25uw6llcyBkZSBjZXMgdm9scy4gVsOpcmlmaWV6IHF1ZSBjZXQgb2JqZXQgY29udGllbnQgYmllbiAyODIgbGlnbmVzLioKCmBgYHtyLCB0aWR5PUZBTFNFfQpKRktfMTJmZXYgPC0gZmxpZ2h0cyAlPiUgCiAgZmlsdGVyKG9yaWdpbiA9PSAiSkZLIiwKICAgICAgICAgbW9udGggPT0gMiwKICAgICAgICAgZGF5ID09IDEyKQoKSkZLXzEyZmV2Cm5yb3coSkZLXzEyZmV2KQpgYGAKCgojIyBFeGVyY2ljZSA2LjQuMwoKKlNlbG9uIHZvdXMsIHF1ZWxsZXMgcmFpc29ucyBwZXV2ZW50IGV4cGxpcXVlciBxdeKAmXVuIHZvbCBxdWkgYSBkw6ljb2xsw6kgbuKAmWFpdCBwYXMgZOKAmWhldXJlIGTigJlhdHRlcnJpc3NhZ2UgPyoKCkxlIHZvbCBzJ2VzdCDDqWNyYXPDqSA/IFBsdXMgdnJhaXNlbWJsYWJsZW1lbnQsIGxlIHZvbCBhIGTDuyBmYWlyZSBkZW1pLXRvdXIgb3Ugw6p0cmUgZMOpcm91dMOpIHN1aXRlIMOgIHVuIHByb2Jsw6htZSB0ZWNobmlxdWUgKG91IG3DqXTDqW8gPykuCgoKIyMgRXhlcmNpY2UgNi41LjYKCioxLiBGYWl0ZXMgdW4gdGFibGVhdSBpbmRpcXVhbnQgY29tYmllbiBkZSB2b2xzIG9udCDDqXTDqSBhbm51bMOpcyBhcHLDqHMgbGUgZMOpY29sbGFnZSwgcG91ciBjaGFxdWUgY29tcGFnbmllIGHDqXJpZW5uZS4qCgpgYGB7ciwgdGlkeT1GQUxTRX0KZmxpZ2h0cyAlPiUgICAgICAgICAgICAgICAgICAgICAjIE9uIHByZW5kIGZsaWdodHMsIHB1aXMKICBmaWx0ZXIoIWlzLm5hKGRlcF90aW1lKSwgICAgICAjIE9uIGZpbHRyZSBsZXMgdm9scyBheWFudCB1bmUgaGV1cmUgZGUgZMOpY29sbGFnZQogICAgICAgICBpcy5uYShhcnJfdGltZSkpICU+JSAgICMgTWFpcyBwYXMgZCdoZXVyZSBkJ2Fycml2w6llLCBwdWlzCiAgZ3JvdXBfYnkoY2FycmllcikgJT4lICAgICAgICAgIyBPbiBncm91cGUgcGFyIGNvbXBhZ25pZSBhw6lyaWVubmUsIHB1aXMKICBzdW1tYXJpemUoY2FuY2VsbGVkID0gbigpKSAgICAjIE9uIGNvbXB0ZSBsZSBub21icmUgZGUgbGlnbmVzIHBhciBncm91cGUKYGBgCgoqMi4gRmFpdGVzIHVuIHRhYmxlYXUgaW5kaXF1YW50IGxlcyB2aXRlc3NlcyBkZSB2ZW50cyBtaW5pbWFsZXMsIG1heGltYWxlcyBldCBtb3llbm5lcywgZW5yZWdpc3Ryw6llcyBjaGFxdWUgbW9pcyBkYW5zIGNoYXF1ZSBhw6lyb3BvcnQgZGUgTmV3IFlvcmsuKgoKYGBge3IsIHRpZHk9RkFMU0V9CndlYXRoZXIgJT4lIAogIGdyb3VwX2J5KG9yaWdpbiwgbW9udGgpICU+JSAKICBzdW1tYXJpemUobWF4X3dpbmQgPSBtYXgod2luZF9zcGVlZCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgbWluX3dpbmQgPSBtaW4od2luZF9zcGVlZCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgbW95X3dpbmQgPSBtZWFuKHdpbmRfc3BlZWQsIG5hLnJtID0gVFJVRSkpCmBgYAoKKjMuIFNhY2hhbnQgcXVlIGxlcyB2aXRlc3NlcyBkdSB2ZW50IHNvbnQgZXhwcmltw6llcyBlbiBtaWxlcyBwYXIgaGV1cmUsIGNlcnRhaW5lcyB2YWxldXJzIHNvbnQtZWxsZXMgc3VycHJlbmFudGVzID8gw4AgbCdhaWRlIGRlIGxhIGZvbmN0aW9uIGBmaWx0ZXIoKWAsIMOpbGltaW5leiBsYSBvdSBsZXMgdmFsZXVycyBhYmVycmFudGVzLioKClVuZSB2YWxldXIgZGUgcGx1cyBkZSAxMDAwIG1waCBlc3QgaW1wb3NzaWJsZS4gSWwgcydhZ2l0IHZyYWlzZW1ibGFibGVtZW50IGQndW5lIGVycmV1ciBkZSBzYWlzaWUgb3UgZCdlbnJlZ2lzdHJlbWVudC4KCmBgYHtyLCB0aWR5PUZBTFNFfQp3ZWF0aGVyICU+JQogIGZpbHRlcih3aW5kX3NwZWVkIDw9IDUwMCkgJT4lIAogIGdyb3VwX2J5KG9yaWdpbiwgbW9udGgpICU+JSAKICBzdW1tYXJpc2UobWF4X3dpbmQgPSBtYXgod2luZF9zcGVlZCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgbWluX3dpbmQgPSBtaW4od2luZF9zcGVlZCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgbW95X3dpbmQgPSBtZWFuKHdpbmRfc3BlZWQsIG5hLnJtID0gVFJVRSkpCmBgYAoKKjQuIEVuIHV0aWxpc2FudCBsZXMgZG9ubsOpZXMgZGUgdml0ZXNzZSBkZSB2ZW50IGR1IHRhYmxlYXUgYHdlYXRoZXJgLCBwcm9kdWlzZXogbGUgZ3JhcGhpcXVlIHN1aXZhbnQuKgoKYGBge3Igd2luZHNwZWVkLCB0aWR5PUZBTFNFLCB3YXJuaW5nID0gRkFMU0V9CndlYXRoZXIgJT4lIAogIGZpbHRlcih3aW5kX3NwZWVkIDwgNTAwKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gZmFjdG9yKG1vbnRoKSwgeSA9IHdpbmRfc3BlZWQpKSArCiAgZ2VvbV9qaXR0ZXIod2lkdGggPSAwLjIsIGhlaWdodCA9IDAsIGFscGhhID0gMC4yKSArCiAgbGFicyh4ID0gIk1vaXMiLAogICAgICAgeSA9ICJWaXRlc3NlIGR1IHZlbnQgKG1waCkiKQpgYGAKCipTZWxvbiB2b3VzLCBwb3VycXVvaSBsZXMgcG9pbnRzIHNvbnQtaWxzIG9yZ2FuaXPDqXMgZW4gYmFuZGVzIGhvcml6b250YWxlcyA/ICAqCipTZWxvbiB2b3VzLCBwb3VycXVvaSBuJ3kgYSB0J2lsIGphbWFpcyBkZSB2ZW50IGVudHJlIDAgZXQgZW52aXJvbiAzIG1pbGVzIMOgIGwnaGV1cmUgKG1waCkgPyoKCkwnYXBwYXJlaWwgZGUgbWVzdXJlcyBuJ2VucmVnaXN0cmUgcXVlIGRlcyB2aXRlc3NlcyBkZSB2ZW50IHBhciBpbmNyw6ltZW50IGZpeGUgZXQgc29uIHNldWlsIGRlIGTDqXRlY3Rpb24gZXN0IHN1cMOpcmlldXIgw6AgZGVzIHZpdGVzc2VzIGRlIDNtcGguIENlbGEgZXhwbGlxdWUgbGVzIGJhbmRlcyBldCBsZSB0cm91IGVudHJlIDAgZXQgMyBtcGguCgoqU2FjaGFudCBxdeKAmWVuIGRpdmlzYW50IGRlcyBtcGggcGFyIDEuMTUxIG9uIG9idGllbnQgZGVzIHZpdGVzc2VzIGVuIG7Fk3VkcywgcXVlIG5vdXMgYXBwcmVuZCBjZXR0ZSBjb21tYW5kZSA6KgoKYGBge3J9CnNvcnQodW5pcXVlKHdlYXRoZXIkd2luZF9zcGVlZCkpLzEuMTUxCmBgYAoKSWNpLCBvbiBjb252ZXJ0aXQgbGVzIGRvbm7DqWVzIGRlIHZpdGVzc2UgZGUgdmVudCBkZSBtcGggZW4gbsWTdWRzLiBMYSBmb25jdGlvbiBgdW5pcXVlKClgIHBlcm1ldCBkJ8OpbGltaW5lciBsZXMgZHVwbGljYXRzIGV0IGBzb3J0KClgIHRyaWUgbGVzIGRvbm7DqWVzIGVuIG9yZHJlIGNyb2lzc2FudC4gT24gdm9pdCBkb25jIHF1ZSBzZXVsZXMgcXVlbHF1ZXMgdmFsZXVycyBkZSB2ZW50IHNvbnQgZW5yZWdpc3Ryw6llcy4gTCdlbnJlZ2lzdHJldXIgZXN0IGluYWNwYWJsZSBkZSBkw6l0ZWN0ZXIgbW9pbnMgZGUgMyBuxZN1ZHMgZGUgdmVudCwgZXQgZW5zdWl0ZSwgaWwgZW5yZWdpc3RyZSBkZXMgdmFsZXVycyBlbnRpw6hyZXMgKDMsIDQsIDUsIGV0IGFpbnNpIGRlIHN1aXRlIGp1c3F1J2F1IG1heGltdW0gZGUgMzcgbsWTdWRzKS4KCiMjIEV4ZXJjaWNlIDYuNy4yCgoqMS4gRGFucyBgZ2dwbG90MmAgbGUgamV1IGRlIGRvbm7DqWVzIGBtcGdgIGNvbnRpZW50IGRlcyBpbmZvcm1hdGlvbnMgc3VyIDIzNCBtb2TDqGxlcyBkZSB2b2l0dXJlcy4gRXhhbWluZXogY2UgamV1IGRlIGRvbm7DqWVzIGF2ZWMgbGEgZm9uY3Rpb24gYFZpZXcoKWAgZXQgY29uc3VsdGV6IGwnYWlkZSBkZSBjZSBqZXUgZGUgZG9ubsOpZXMgcG91ciBzYXZvaXIgw6AgcXVvaSBjb3JyZXNwb25kZW50IGxlcyBkaWZmw6lyZW50ZXMgdmFyaWFibGVzLiBRdWVsbGUocykgdmFyaWFibGUocykgbm91cyByZW5zZWlnbmVudCBzdXIgbGEgY29uc29tbWF0aW9uIGRlcyB2w6loaWN1bGVzID8gw4AgcXVvaSBjb3JyZXNwb25kIGxhIHZhcmlhYmxlIGBkaXNwYCA/KgoKTGVzIHZhcmlhYmxlcyBgaHd5YCBldCBgY3R5YCBub3VzIHJlbnNlaWduZW50IHN1ciBsYSBjb25zb21tYXRpb24gZGVzIHbDqWhpY3VsZXMgc3VyIGF1dG9yb3V0ZSBldCBlbiB2aWxsZSByZXNwZWN0aXZlbWVudC4gTGVzIGNvbnNvbW1hdGlvbnMgc29udCBkb25uw6llcyBlbiBtaWxlcyBwZXIgZ2Fsb24uIGBkaXNwYCBlc3QgbGEgY3lsaW5kcsOpZSBkdSBtb3RldXIsIHNvbiB2b2x1bWUgZW4gbGl0cmVzLgoKKjIuIExhIGNvbnNvbW1hdGlvbiBlc3QgZG9ubsOpZSBlbiBtaWxlcyBwYXIgZ2FsbG9uLiBDcsOpZXogdW5lIG5vdXZlbGxlIHZhcmlhYmxlIGBjb25zb2AgcXVpIGNvbnRpZW5kcmEgbGEgY29uc29tbWF0aW9uIGV4cHJpbcOpZSBlbiBub21icmUgZGUgbGl0cmVzIHBvdXIgMTAwIGtpbG9tw6h0cmVzLioKCmBgYHtyIHRpZHk9RkFMU0V9Cm1wZyAlPiUgCiAgbXV0YXRlKGNvbnNvID0gMjM1LjIxNSAvIGh3eSkKYGBgCgoqMy4gRmHDrnRlcyB1biBncmFwaGlxdWUgcHLDqXNlbnRhbnQgbGEgcmVsYXRpb24gZW50cmUgbGEgY3lsaW5kcsOpZSBlbiBsaXRyZXMgZXQgbGEgY29uc29tbWF0aW9uIHN1ciBhdXRvcm91dGUgZXhwcmltw6llIGVuIG5vbWJyZSBkZSBsaXRyZXMgcG91ciAxMDAga2lsb23DqHRyZXMuIFZvdXMgZXhjbHVlcmV6IGxlcyB2w6loaWN1bGVzIGRvbnQgbGEgYGNsYXNzYGUgZXN0IGAyc2VhdGVyYCBkZSBjZSBncmFwaGlxdWUgKGlsIHMnYWdpdCBkZSB2b2l0dXJlcyBkZSBzcG9ydHMgdHLDqHMgY29tcGFjdGVzIHF1J2lsIGVzdCBkaWZmaWNpbGUgZGUgbWVzdXJlciBhdXggYXV0cmVzKS4gU3VyIHZvdHJlIGdyYXBoaXF1ZSwgbGEgY291bGV1ciBkZXZyYWl0IHJlcHLDqXNlbnRlciBsZSB0eXBlIGRlIHbDqWhpY3VsZS4gVm91cyBham91dGVyZXogdW5lIGRyb2l0ZSBkZSByw6lncmVzc2lvbiBlbiB1dGlsaXNhbnQgYGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIpYC4qCgpgYGB7ciBjb25zb21tYXRpb24sIHRpZHk9RkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0KbXBnICU+JSAKICBmaWx0ZXIoY2xhc3MgIT0gIjJzZWF0ZXIiKSAlPiUgCiAgbXV0YXRlKGNvbnNvID0gMjM1LjIxNSAvIGh3eSkgJT4lIAogIGdncGxvdChhZXMoeCA9IGRpc3BsLCB5ID0gY29uc28pKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBjbGFzcykpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iKSArCiAgbGFicyh4ID0gIkN5bGluZHLDqWUgKHZvbHVtZSBkdSBtb3RldXIgZW4gbGl0cmVzKSIsCiAgICAgICB5ID0gIkNvbnNvbW1hdGlvbiAobGl0cmVzIHBvdXIgMTAwIGtpbG9tw6h0cmVzKSIsCiAgICAgICBjb2xvciA9ICJUeXBlIGRlXG52w6loaWN1bGUiLAogICAgICAgdGl0bGUgPSAiUmVsYXRpb24gcG9zaXRpdmUgZW50cmUgY3lsaW5kcsOpZSBldCBjb25zb21tYXRpb24iKSArCiAgdGhlbWVfbWluaW1hbChiYXNlX2ZhbWlseSA9ICJHaWxsIFNhbnMiKQpgYGAKCio0LiBDZSBncmFwaGlxdWUgcHLDqXNlbnRlLXQnaWwgY29ycmVjdGVtZW50IGwnZW5zZW1ibGUgZGVzIGRvbm7DqWVzIGRlIGNlcyAyIHZhcmlhYmxlcyA/IFBvdXJxdW9pID8gQ29tcGFyZXogbGUgZ3JhcGhpcXVlIGRlIGxhIHF1ZXN0aW9uIDMgY2ktZGVzc3VzIGV0IGxlIGdyYXBoaXF1ZSBwcsOpc2VudMOpIGNpLWRlc3NvdXMuIFNlbG9uIHZvdXMsIHF1ZWxzIGFyZ3VtZW50cyBldC9vdSBmb25jdGlvbnMgb250IMOpdMOpIG1vZGlmacOpcyBwb3VyIGFycml2ZXIgw6AgY2Ugbm91dmVhdSBncmFwaGlxdWUgPyBRdWVscyBzb250IGxlcyBhdmFudGFnZXMgZXQgbGVzIGluY29udsOpbmllbnRzIGRlIGNlIGdyYXBoaXF1ZSBwYXIgcmFwcG9ydCBhdSBwcsOpY8OpZGVudCA/CgpgYGB7ciBjb25zb21tYXRpb24yLCB0aWR5PUZBTFNFLCB3YXJuaW5nID0gRkFMU0V9Cm1wZyAlPiUgCiAgZmlsdGVyKGNsYXNzICE9ICIyc2VhdGVyIikgJT4lIAogIG11dGF0ZShjb25zbyA9IDIzNS4yMTUgLyBod3kpICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBkaXNwbCwgeSA9IGNvbnNvKSkgKwogIGdlb21faml0dGVyKGFlcyhmaWxsID0gY2xhc3MpLCBzaGFwZSA9IDIxLCB3aWR0aCA9IDAuMDUsIGhlaWdodCA9IDAuMDUsIGFscGhhID0gMC43KSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIikgKwogIGxhYnMoeCA9ICJDeWxpbmRyw6llICh2b2x1bWUgZHUgbW90ZXVyIGVuIGxpdHJlcykiLAogICAgICAgeSA9ICJDb25zb21tYXRpb24gKGxpdHJlcyBwb3VyIDEwMCBraWxvbcOodHJlcykiLAogICAgICAgZmlsbCA9ICJUeXBlIGRlXG52w6loaWN1bGUiLAogICAgICAgdGl0bGUgPSAiUmVsYXRpb24gcG9zaXRpdmUgZW50cmUgY3lsaW5kcsOpZSBldCBjb25zb21tYXRpb24iKSArCiAgdGhlbWVfbWluaW1hbChiYXNlX2ZhbWlseSA9ICJHaWxsIFNhbnMiKQpgYGAKCk9uIHV0aWxpc2UgYGdlb21faml0dGVyKClgIGF1IGxpZXUgZGUgYGdlb21fcG9pbnQoKWAgcG91ciBxdWUgbGVzIHBvaW50cyBuZSBzZSBzdXBlcnBvc2VudCBwbHVzLiBPbiB2b2l0IGRvbmMgbWlldXggbGVzIGRvbm7DqWVzLiBOw6lhbm1vaW5zLCBsZXMgcG9pbnRzIHNvbnQgbMOpZ8OocmVtZW50IGTDqXBsYWPDqXMsIGNlIGdyYXBoaXF1ZSBlc3QgZG9uYyBtb2lucyBwcsOpY2lzIHF1ZSBsZSBwcmVtaWVyLiBJbCBlc3QgcGx1cyBpbmV4YWN0LgoKTGVzIHN5bWJvbGVzIG9udCBhdXNzaSDDqXTDqSBjaGFuZ8OpcyA6IG9uIGVzdCBwYXNzw6kgw6AgZGVzIHBvaW50cyBhdmVjIGNvbnRvdXIgKGBzaGFwZSA9IDIxYCkgYXVxdWVscyBvbiBhIGF0dHJpYnXDqSB1bmUgdHJhbnNwYXJlbmNlICh0b3Vqb3VycyBwb3VyIG1pZXV4IHZpc3VhbGlzZXIgbGVzIGNoZXZhdWNoZW1lbnRzIGRlIHBvaW50cykuIER1IGNvdXAsIGNlIG4nZXN0IHBsdXMgbGEgYGNvbG9yYCBxdWkgZXN0IGFzc29jacOpZSDDoCBsYSBjbGFzc2UgZGUgdsOpaGljdWxlLCBtYWlzIGBmaWxsYCwgbGEgY291bGV1ciBkZSByZW1wbGlzc2FnZS4KCgojIyBFeGVyY2ljZSA2LjEwCgoqMS4gQ3LDqWV6IHVuIHRhYmxlYXUgYGRlbGF5ZWRgIGluZGlxdWFudCwgcG91ciBjaGFxdWUgY29tcGFnbmllIGHDqXJpZW5uZSBldCBjaGFxdWUgbW9pcyBkZSBsJ2FubsOpZSwgbGUgbm9tYnJlIGRlIHZvbHMgYXlhbnQgZXUgdW4gcmV0YXJkIHN1cMOpcmlldXIgw6AgMzAgbWludXRlcyDDoCBsJ2Fycml2w6llIMOgIGRlc3RpbmF0aW9uLiBDZSB0YWJsZWF1IGRldnJhaXQgY29udGVuaXIgdW5pcXVlbWVudCAzIGNvbG9ubmVzIDoqCgoqIGBjYXJyaWVyYCA6IGxhIGNvbXBhZ25pZSBhw6lyaWVubmUKKiBgbW9udGhgIDogbGUgbW9pcyBkZSBsJ2FubsOpZSAyMDEzCiogYG5fZGVsYXllZGAgOiBsZSBub21icmUgZGUgdm9scyBheWFudCBwbHVzIGRlIDMwIG1pbnV0ZXMgZGUgcmV0YXJkCgoKYGBge3IsIHRpZHk9RkFMU0V9CiMgQ2FsY3VsIGR1IG5vbWJyZSBkZSB2b2xzIGVuIHJldGFyZCAoKyBkZSAzMCBtaW4gw6AgbCdhcnJpdsOpZSkgcG91ciBjaGFxdWUgY29tcGFnbmllIGV0IGNoYXF1ZSBtb2lzCmRlbGF5ZWQgPC0gZmxpZ2h0cyAlPiUKICBmaWx0ZXIoYXJyX2RlbGF5ID4gMzApICU+JQogIGdyb3VwX2J5KGNhcnJpZXIsIG1vbnRoKSAlPiUKICBzdW1tYXJpemUobl9kZWxheWVkID0gbigpLCAuZ3JvdXBzID0gImRyb3AiKQoKZGVsYXllZApgYGAKCgoqMi4gQ3LDqWV6IHVuIHRhYmxlYXUgYHRvdGFsYCBpbmRpcXVhbnQgbGUgbm9tYnJlIHRvdGFsIGRlIHZvbHMgYWZmcsOpdMOpcyAoZXQgbm9uIGFubnVsw6lzKSBwYXIgY2hhcXVlIGNvbXBhZ25pZSBhw6lyaWVubmUgZXQgY2hhcXVlIG1vaXMgZGUgbCdhbm7DqWUuIENlIHRhYmxlYXUgZGV2cmFpdCBjb250ZW5pciBzZXVsZW1lbnQgMyBjb2xvbm5lcyA6KgoKKiBgY2FycmllcmAgOiBsYSBjb21wYWduaWUgYcOpcmllbm5lCiogYG1vbnRoYCA6IGxlIG1vaXMgZGUgbCdhbm7DqWUgMjAxMwoqIGBuX3RvdGFsYCA6IGxlIG5vbWJyZSB0b3RhbCBkZSB2b2xzIGFycml2w6lzIMOgIGRlc3RpbmF0aW9uCgpgYGB7ciwgdGlkeT1GQUxTRX0KIyBDYWxjdWwgZHUgbm9tYnJlIHRvdGFsIGRlIHZvbHMgbm9uIGFubnVsw6lzIHBvdXIgY2hhcXVlIGNvbXBhZ25pZSBldCBjaGFxdWUgbW9pcwp0b3RhbCA8LSBmbGlnaHRzICU+JQogIGZpbHRlcighaXMubmEoYXJyX2RlbGF5KSkgJT4lCiAgZ3JvdXBfYnkoY2FycmllciwgbW9udGgpICU+JQogIHN1bW1hcml6ZShuX3RvdGFsID0gbigpKQoKdG90YWwKYGBgCgoKKjMuIEZ1c2lvbm5leiBjZXMgMiB0YWJsZWF1eCBlbiByw6lhbGlzYW50IGxhIGpvaW50dXJlIGFwcHJvcHJpw6llLiBMZSB0YWJsZWF1IGZpbmFsLCBxdWUgdm91cyBub21tZXJleiBgY2Fycmllcl9zdGF0c2AgZGV2cmFpdCBjb250ZW5pciAxODUgbGlnbmVzLiBTaSBjZXJ0YWluZXMgY29sb25uZXMgY29udGllbm5lbnQgZGVzIGRvbm7DqWVzIG1hbnF1YW50ZXMsIHJlbXBsYWNlei1sZXMgcGFyIGRlcyAwIMOgIGwnYWlkZSBkZXMgZm9uY3Rpb25zIGBtdXRhdGUoKWAgZXQgYG5hX3JlcGxhY2UoKWAuKgoKKjQuIEFqb3V0ZXogw6Agdm90cmUgdGFibGVhdSBgY2Fycmllcl9zdGF0c2AgdW5lIHZhcmlhYmxlIGByYXRlYCBxdWkgY29udGllbnQgbGEgcHJvcG9ydGlvbiBkZSB2b2xzIGFycml2w6lzIMOgIGRlc3RpbmF0aW9uIGF2ZWMgcGx1cyBkZSAzMCBtaW51dGVzIGRlIHJldGFyZCwgcG91ciBjaGFxdWUgY29tcGFnbmllIGHDqXJpZW5uZSBldCBjaGFxdWUgbW9pcyBkZSBsJ2FubsOpZS4qCgoqNS4gQWpvdXRleiDDoCB2b3RyZSB0YWJsZWF1IGBjYXJyaWVyX3N0YXRzYCBsZSBub20gY29tcGxldCBkZXMgY29tcGFnbmllcyBhw6lyaWVubmVzIGVuIHLDqWFsaXNhbnQgbGEgam9pbnR1cmUgYXBwcm9wcmnDqWUgYXZlYyBsZSB0YWJsZWF1IGBhaXJsaW5lc2AuKgoKYGBge3IsIHRpZHk9RkFMU0V9CiMgQ3LDqWF0aW9uIGR1IHRhYmxlYXUgZGUgc3ludGjDqHNlICgyIGxlZnRfam9pbigpKQpjYXJyaWVyX3N0YXRzIDwtIHRvdGFsICU+JQogIGxlZnRfam9pbihkZWxheWVkKSAlPiUKICBtdXRhdGUobl9kZWxheWVkID0gcmVwbGFjZV9uYShuX2RlbGF5ZWQsIDApLAogICAgICAgICByYXRlID0gbl9kZWxheWVkIC8gbl90b3RhbCkgJT4lCiAgbGVmdF9qb2luKGFpcmxpbmVzKQoKY2Fycmllcl9zdGF0cwpgYGAKCgoqNi4gRmFpdGVzIHVuIGdyYXBoaXF1ZSBzeW50aMOpdGlxdWUgcHLDqXNlbnRhbnQgY2VzIHLDqXN1bHRhdHMgZGUgbGEgZmHDp29uIGxhIHBsdXMgY2xhaXJlIHBvc3NpYmxlKgoKYGBge3IsIHRpZHk9RkFMU0V9CiMgQ3LDqWF0aW9uIGRlIGdyYXBoaXF1ZXMgZGUgc3ludGjDqHNlCmNhcnJpZXJfc3RhdHMgJT4lCiAgZ2dwbG90KGFlcyh4ID0gZmFjdG9yKG1vbnRoKSwgeSA9IHJhdGUsIGdyb3VwID0gMSkpICsKICBnZW9tX2xpbmUoKSArCiAgZ2VvbV9wb2ludCgpICsKICBmYWNldF93cmFwKH5uYW1lLCBuY29sPTQpICsKICBsYWJzKHggPSAiTW9pcyIsCiAgICAgICB5ID0gIlByb3BvcnRpb24gZGUgdm9scyBhcnJpdmFudCBlbiByZXRhcmRcbihwbHVzIGRlIDMwIG1pbikiKSArCiAgdGhlbWVfYncoYmFzZV9mYW1pbHkgPSAiR2lsbCBTYW5zIikKYGBgCgoKKjcuIFF1ZWxsZSBjb21wYWduaWUgYcOpcmllbm5lIHNlbWJsZSBzZSBjb21wb3J0ZXIgdHLDqHMgZGlmZsOpcmVtbWVudCBkZXMgYXV0cmVzID8gw4AgcXVvaSBwb3V2ZXotdm91cyBhdHRyaWJ1ZXIgY2UgY29tcG9ydGVtZW50IGF0eXBpcXVlID8qCgpMYSBjb21wYWduaWUgYE9PYCAoU2t5V2VzdCBBaXJsaW5lcyBJbmMuKSBhIHVuIGNvbXBvcnRlbWVudCB0csOocyBhdHlwaXF1ZSBkw7sgYXUgdHLDqHMgZmFpYmxlIG5vbWJyZSBkZSB2b2xzIGFmZnLDqXTDqXMgKDEgc2V1bCBlbiBqYW52aWVyLCAyIGVuIGp1aW4sIDQgZW4gYW/Du3QsIDE3IGVuIHNlcHRlbWJyZSBldCA1IGVuIG5vdmVtYnJlKS4gRWxsZSBuJ2VzdCBkJ2FpbGxldXJzIHByw6lzZW50ZSBxdWUgcXVlbHF1ZXMgbW9pcyBkZSBsJ2FubsOpZSBkYW5zIGxlcyBhw6lyb3BvcnRzIGRlIE5ldyBZb3JrLgpgYGB7ciwgdGlkeT1GQUxTRX0KY2Fycmllcl9zdGF0cyAlPiUKICBmaWx0ZXIoY2FycmllciA9PSAiT08iKQpgYGAKCgoqOC4gUG91ciBsZXMgY29tcGFnbmllcyBhZmZyw6l0YW50IHVuIGdyYW5kIG5vbWJyZSBkZSB2b2xzIGNoYXF1ZSBhbm7DqWUgKGUuZy4gYFVBYCwgYEI2YCBldCBgRVZgKSwgcXVlbGxlcyBzb250IGxlcyBww6lyaW9kZXMgb8O5IGxlcyBwbHVzIGZvcnRlcyBwcm9wb3J0aW9ucyBkZSB2b2xzIGVuIHJldGFyZCBzb250IG9ic2VydsOpZXMgPyBFdCBsZXMgcGx1cyBmYWlibGVzID8gUXVlbGxlKHMpIGh5cG90aMOoc2UocykgcG91dmV6LXZvdXMgZm9ybXVsZXIgcG91ciBleHBsaXF1ZXIgY2VzIG9ic2VydmF0aW9ucyA/KgoKUG91ciBsZXMgcGx1cyBncm9zc2VzIGNvbXBhZ25pZXMsIGxlcyByZXRhcmRzIGxlcyBwbHVzIGZyw6lxdWVudHMgc29udCBvYnNlcnbDqXMgbCfDqXTDqSwgZXQgbGVzIG1vaW5zIGZyw6lxdWVudHMgZW4gYXV0b21uZS4gQ2VsYSBjb3JyZXNwb25kIGF1eCBww6lyaW9kZXMgZGUgdHLDqHMgZm9ydGVzIGFmZmx1ZW5jZXMgZW4gw6l0w6ksIGV0IGF1IG1vaW5zIGZvcnRlcyBhZmZsdWVuY2VzIMOgIGxhIHJlbnRyw6llIGRlIHNlcHRlbWJyZS4gQydlc3QgZHUgbW9pbnMgbGUgY2FzIHBvdXIgYFVBYCBldCBgQjZgLiBNb2lucyBwb3VyIGBFVmAuCgpgYGB7ciwgdGlkeT1GQUxTRX0KY2Fycmllcl9zdGF0cyAlPiUKICBmaWx0ZXIoY2FycmllciAlaW4lIGMoIlVBIiwgIkI2IiwgIkVWIikpICU+JQogIHNlbGVjdChjYXJyaWVyLCBtb250aCwgbl90b3RhbCkgJT4lCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IGNhcnJpZXIsIAogICAgICAgICAgICAgIHZhbHVlc19mcm9tID0gbl90b3RhbCkKYGBgCgpgYGB7ciwgdGlkeT1GQUxTRX0KY2Fycmllcl9zdGF0cyAlPiUKICBmaWx0ZXIoY2FycmllciAlaW4lIGMoIlVBIiwgIkI2IiwgIkVWIikpICU+JQogIGdncGxvdChhZXMoeCA9IGZhY3Rvcihtb250aCksIHkgPSBuX3RvdGFsLCBncm91cCA9IGNhcnJpZXIsIGNvbG9yID0gY2FycmllcikpICsKICAgIGdlb21fbGluZSgpCmBgYAoKKjkuIEZhaXRlcyB1biB0YWJsZWF1IHN5bnRow6l0aXF1ZSBwcsOpc2VudGFudCBjZXMgcsOpc3VsdGF0cyBkZSBsYSBmYcOnb24gbGEgcGx1cyBjb21wYWN0ZSBldCBjbGFpcmUgcXVlIHBvc3NpYmxlLCBhZmluIHBhciBleGVtcGxlIGRlIGxlcyBpbnTDqWdyZXIgw6AgdW4gcmFwcG9ydC4qCgpgYGB7ciwgdGlkeT1GQUxTRX0KIyBDcsOpYXRpb24gZHUgdGFibGVhdSBzeW50aMOpdGlxdWUuIEonYXJyb25kaXMgw6AgMyBjaGlmZnJlcyBzaWduaWZpY2F0aWZzLgpjYXJyaWVyX3N0YXRzICU+JQogIHNlbGVjdChuYW1lLCBtb250aCwgcmF0ZSkgJT4lCiAgbXV0YXRlKHJhdGUgPSByb3VuZChyYXRlLCAzKSkgJT4lCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IG1vbnRoLCAKICAgICAgICAgICAgICB2YWx1ZXNfZnJvbSA9IHJhdGUpCmBgYAoK